문제 해결을 위해 배열을 사용하려 했으나 메모리 초과가 발생하여, 원본 데이터를 직접 수정하며 슬라이딩 윈도우 방식으로 접근했다. 특히 경계 조건 처리는 항상 어려운 부분이지만, 핵심은 구간 커버리지 문제에서 각 구간의 우측 끝점을 기준으로 왼쪽으로 확장 가능한 최대 길이를 탐색하는 것이다.
주요 전략은 다음과 같다:
- 먼저 타일의 시작 위치 기준으로 정렬한다.
- 왼쪽 포인터를 유지하면서, 현재 오른쪽 포인터가 가리키는 구간의 끝점까지의 총 덮개 길이를 누적한다.
- 현재 카펫 길이로 왼쪽 구간이 완전히 커버되지 않으면, 왼쪽 포인터를 이동시키며 커버되지 않는 부분을 제거한다.
- 그 후, 겹치지 않는 부분(카펫 범위 밖)을 계산해 전체 커버리지에서 빼서 최댓값 갱신.
class Solution {
public:
int maximumWhiteTiles(vector<vector<int>>& tiles, int carpetLen) {
sort(tiles.begin(), tiles.end());
int left = 0;
int covered = 0;
int result = 0;
for (auto& tile : tiles) {
int start = tile[0];
int end = tile[1];
covered += end - start + 1;
// 현재 카펫이 왼쪽 구간의 끝보다 앞선 경우, 왼쪽 포인터 이동
while (tiles[left][1] + carpetLen - 1 < end) {
covered -= tiles[left][1] - tiles[left][0] + 1;
left++;
}
// 카펫이 겹치지 않는 부분 계산 (음수 방지)
int uncovered = max(end - carpetLen + 1 - tiles[left][0], 0);
result = max(result, covered - uncovered);
}
return result;
}
};
슬라이딩 윈도우 문제에서는 반드시 정렬이 첫 단계다. 이 문제는 두 차원의 구간을 다루므로, 포인터는 단순한 인덱스가 아니라 vector<int>& 형태의 구간을 의미한다.
또한, 유사한 유형의 문제인 "최대 과일 수 수확" (LeetCode 2306)을 참고하면, 더 깔끔한 슬라이딩 윈도우 구현이 가능하다. 아래는 영신님의 간결한 코드 예시이다:
class Solution {
public:
int maxTotalFruits(vector<vector<int>>& fruits, int startPos, int k) {
int left = lower_bound(fruits.begin(), fruits.end(), startPos - k,
[](const auto& a, int val) { return a[0] < val; }) - fruits.begin();
int right = left, sum = 0, n = fruits.size();
// startPos 이전까지의 과일 수 누적
while (right < n && fruits[right][0] <= startPos) {
sum += fruits[right][1];
right++;
}
int ans = sum;
// 오른쪽으로 확장하며 최대값 갱신
while (right < n && fruits[right][0] <= startPos + k) {
sum += fruits[right][1];
// 왼쪽 구간이 도달 불가능한 경우, left 포인터 이동
while (fruits[right][0] * 2 - fruits[left][0] - startPos > k &&
fruits[right][0] - fruits[left][0] * 2 + startPos > k) {
sum -= fruits[left][1];
left++;
}
ans = max(ans, sum);
right++;
}
return ans;
}
};
이 코드는 시간 복잡도를 효율적으로 관리하며, 조건에 따라 왼쪽 포인터를 적절히 이동시켜 최적의 해를 찾는다.