하이쿠 조건을 만족하는 구간 존재 여부 판별 알고리즘

O(n log n) 이분 탐색 기법

누적 합 배열을 활용하여 각 시작 인덱스별로 X, Y, Z 합 구간의 종료 지점을 전처리합니다. 이진 탐색을 통해 정확히 X, Y, Z에 해당하는 부분 합의 끝 위치를 계산한 후, 연속된 세 구간이 조건을 만족하는지 O(n) 시간에 검증합니다.

#include <iostream>
#include <vector>
#include <climits>
using namespace std;
typedef long long ll;

int main() {
    ll num, targetX, targetY, targetZ;
    cin >> num >> targetX >> targetY >> targetZ;
    vector<ll> prefix(num + 1, 0);
    vector<ll> endX(num + 1, LONG_MAX);
    vector<ll> endY(num + 1, LONG_MAX);
    vector<ll> endZ(num + 1, LONG_MAX);

    for (int idx = 1; idx <= num; idx++) {
        ll val;
        cin >> val;
        prefix[idx] = prefix[idx - 1] + val;
    }

    for (int start = 1; start <= num; start++) {
        int low = start, high = num;
        while (low <= high) {
            int mid = (low + high) / 2;
            ll current = prefix[mid] - prefix[start - 1];
            if (current < targetX) low = mid + 1;
            else if (current > targetX) high = mid - 1;
            else {
                endX[start] = mid;
                break;
            }
        }
    }

    // Y, Z 대상 동일 이분 탐색 로직
    for (int start = 1; start <= num; start++) { 
        /* Y에 대한 탐색 코드 */ 
    }
    for (int start = 1; start <= num; start++) { 
        /* Z에 대한 탐색 코드 */ 
    }

    for (int i = 1; i <= num; i++) {
        if (endX[i] == LONG_MAX || endX[i] >= num) continue;
        int nextStart = endX[i] + 1;
        if (endY[nextStart] == LONG_MAX || endY[nextStart] >= num) continue;
        if (endZ[endY[nextStart] + 1] != LONG_MAX) {
            cout << "Yes";
            return 0;
        }
    }
    cout << "No";
    return 0;
}

O(n) 투 포인터 최적화

단일 패스로 각 시작점에 대한 종료 지점을 계산합니다. 포인터를 이동시키며 현재 구간 합이 목표값보다 작을 때만 우측으로 진행합니다. 모든 포인터가 총 n번만 이동하므로 선형 시간 복잡도를 보장합니다.

#include <iostream>
#include <vector>
#include <climits>
using namespace std;
typedef long long ll;

int main() {
    ll num, targetX, targetY, targetZ;
    cin >> num >> targetX >> targetY >> targetZ;
    vector<ll> prefix(num + 1, 0);
    vector<ll> endX(num + 1, LONG_MAX);
    vector<ll> endY(num + 1, LONG_MAX);
    vector<ll> endZ(num + 1, LONG_MAX);

    for (int idx = 1; idx <= num; idx++) {
        ll val;
        cin >> val;
        prefix[idx] = prefix[idx - 1] + val;
    }

    int ptrX = 1;
    for (int i = 1; i <= num; i++) {
        while (ptrX <= num && prefix[ptrX] - prefix[i - 1] < targetX) 
            ptrX++;
        if (prefix[ptrX] - prefix[i - 1] == targetX) 
            endX[i] = ptrX;
    }

    // Y, Z 대상 동일 포인터 로직
    int ptrY = 1;
    for (int i = 1; i <= num; i++) { 
        /* Y에 대한 포인터 처리 */ 
    }
    int ptrZ = 1;
    for (int i = 1; i <= num; i++) { 
        /* Z에 대한 포인터 처리 */ 
    }

    for (int i = 1; i <= num; i++) {
        if (endX[i] == LONG_MAX || endX[i] >= num) continue;
        int next = endX[i] + 1;
        if (endY[next] == LONG_MAX || endY[next] >= num) continue;
        if (endZ[endY[next] + 1] != LONG_MAX) {
            cout << "Yes";
            return 0;
        }
    }
    cout << "No";
    return 0;
}

태그: 누적_합 이분_탐색 투_포인터 AtCoder 알고리즘_최적화

6월 29일 02:27에 게시됨