정올시는 N × N 크기의 정방형 격자로 이루어진 도시이며, 각 지역에서는 최대 몇 개의 조개를 주울 수 있는지가 정해져 있다.
각 거주민은 오른쪽 혹은 위쪽으로만 이동하여 수산시장(0,0)으로 이동하며,
이동 중 지나가는 각 지역에서 최대한 많은 조개를 주운다.
이 문제에서 해결해야 할 것은 다음과 같다:
1. 초기 조개 개수를 기준으로 각 지역에서 최대로 팔 수 있는 조개 개수를 계산한 후, 그 합을 출력한다.
2. 이후 주어지는 변경 명령어 (U r c
, D r c
)에 따라 격자 내 특정 좌표의 최대 조개 개수를 변화시키고, 그에 맞춰 최대 조개 합을 업데이트하여 출력한다.
dp[i][j]
를 만들어, 각 위치에서 수산시장까지 가는 최대 조개 개수를 누적 계산한다.U r c
: (r, c)
위치의 조개 개수를 1 증가.D r c
: (r, c)
위치의 조개 개수를 1 감소.dp[i][j]
값이 바뀌어야 하므로 세그먼트 트리를 활용하여 구간 업데이트를 수행한다.#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define MAX_N 1510
// 세그먼트 트리 구조체
struct SegmentTree {
int base = 1 << 11;
int tree[1 << 12] = {0};
void update(int l, int r, int v) {
l += base; r += base;
while (l <= r) {
if (l & 1) tree[l] += v;
if (~r & 1) tree[r] += v;
l = (l + 1) >> 1;
r = (r - 1) >> 1;
}
}
int query(int p) {
int res = 0;
for (p += base; p; p >>= 1) res += tree[p];
return res;
}
} segment[MAX_N];
int N, dp[MAX_N][MAX_N], leftBound[MAX_N], rightBound[MAX_N];
// 특정 위치에서 값을 얻는 함수
int getValue(int row, int col) {
return dp[row][col] + segment[row].query(col);
}
int main() {
ios_base::sync_with_stdio(0); cin.tie(0);
cin >> N;
ll totalMaxShells = 0;
// 초기 조개 개수 입력 및 DP 계산
for (int i = 1; i <= N; i++)
for (int j = 1; j <= N; j++)
cin >> dp[i][j];
for (int i = 1; i <= N; i++)
for (int j = 1; j <= N; j++) {
dp[i][j] += max(dp[i-1][j], dp[i][j-1]);
totalMaxShells += dp[i][j];
}
cout << totalMaxShells << endl;
// 변화 명령어 처리
for (int iter = 0; iter < N; iter++) {
char operation; int row, col;
cin >> operation >> row >> col;
int valueChange = (operation == 'U' ? 1 : -1);
leftBound[row] = rightBound[row] = col;
for (int i = row + 1; i <= N; i++) leftBound[i] = N + 1, rightBound[i] = 0;
// 오른쪽 방향 최대 조개 값 갱신
for (int curRow = row, curCol = col;;) {
if (curCol < N && max(getValue(curRow, curCol), getValue(curRow - 1, curCol + 1)) + valueChange == max(getValue(curRow, curCol) + valueChange, getValue(curRow - 1, curCol + 1)))
curCol++;
else
curRow++;
if (curRow > N) break;
rightBound[curRow] = curCol;
}
// 왼쪽 방향 최대 조개 값 갱신
for (int curRow = row, curCol = col;;) {
if (curRow < N && max(getValue(curRow, curCol), getValue(curRow + 1, curCol - 1)) + valueChange == max(getValue(curRow, curCol) + valueChange, getValue(curRow + 1, curCol - 1)))
curRow++;
else
curCol++;
if (curCol > N || curCol > rightBound[curRow]) break;
leftBound[curRow] = min(leftBound[curRow], curCol);
}
// 세그먼트 트리 업데이트 및 총 최대 조개 수 조정
for (int i = row; i <= N; i++) {
if (leftBound[i] <= rightBound[i]) {
segment[i].update(leftBound[i], rightBound[i], valueChange);
totalMaxShells += valueChange * (rightBound[i] - leftBound[i] + 1);
}
}
cout << totalMaxShells << endl;
}
return 0;
}
update(l, r, v)
: 특정 구간에 대해 값 증가/감소.query(p)
: 특정 열 ( p )의 변경된 값을 조회.이 문제에서는 동적 프로그래밍(DP)과 세그먼트 트리를 결합하여 격자 형태의 최적화 문제를 해결하는 방법을 익힐 수 있었다.
이 방법을 통해 큰 입력에도 효율적으로 동작하는 최적의 해결 방법을 적용할 수 있었다.