C - スキーコース / Ski Course 解説 by admin
Claude 4.6 Opus (Thinking)Overview
This is a dynamic programming problem where you slide down an \(N \times M\) grid from top to bottom, maximizing the total score of cells you pass through, under a zigzag constraint that “you cannot move in the same direction (left-left or right-right) two consecutive times.”
Analysis
Key Insight
You cannot correctly determine whether the zigzag constraint is satisfied by recording only the position at each row — you must also record which direction you moved in the previous step.
For example, regarding the column change \(d_r = c_{r+1} - c_r\): - \(d_r > 0\) (move right) followed by \(d_{r+1} > 0\) (move right) is forbidden - \(d_r < 0\) (move left) followed by \(d_{r+1} < 0\) (move left) is forbidden - \(d_r = 0\) (go straight) can be followed by movement in any direction
Problem with the Naive Approach
If we simply define dp[r][c] = “maximum score when reaching column \(c\) at row \(r\)”, we cannot account for the zigzag constraint when transitioning to the next row, because the previous movement direction is unknown.
Solution
We add the previous movement direction to the DP state. Since there are 3 possible directions — “left (\(d < 0\))”, “straight (\(d = 0\))”, and “right (\(d > 0\))” — the number of states becomes \(O(N \times M \times 3)\), which fits within the constraints.
Algorithm
We define the DP states as follows:
- \(\text{dp}[c][0]\): maximum score when reaching column \(c\) of the current row via a left move (column decreases)
- \(\text{dp}[c][1]\): maximum score when reaching column \(c\) of the current row via going straight (column unchanged)
- \(\text{dp}[c][2]\): maximum score when reaching column \(c\) of the current row via a right move (column increases)
Transitions
Consider reaching column \(c\) of the next row:
Arriving via left move (coming from column \(c+1\) of the previous row):
The previous movement direction must be straight (1) or right (2) (left→left is forbidden)
\(\text{ndp}[c][0] = \max(\text{dp}[c+1][1],\ \text{dp}[c+1][2]) + G[r][c]\)Arriving via going straight (coming from column \(c\) of the previous row):
The previous movement direction can be anything
\(\text{ndp}[c][1] = \max(\text{dp}[c][0],\ \text{dp}[c][1],\ \text{dp}[c][2]) + G[r][c]\)Arriving via right move (coming from column \(c-1\) of the previous row):
The previous movement direction must be left (0) or straight (1) (right→right is forbidden)
\(\text{ndp}[c][2] = \max(\text{dp}[c-1][0],\ \text{dp}[c-1][1]) + G[r][c]\)
Initialization
At row 1, no movement has occurred yet, so for all columns \(c\), we set \(\text{dp}[c][1] = G[1][c]\) (treated as going straight, allowing transition in any direction afterward).
Answer
Take the maximum value across all columns and all directions in the final row.
Complexity
- Time complexity: \(O(N \times M)\) (constant number of operations per cell)
- Space complexity: \(O(M)\) (only maintaining DP arrays for the current row and the next row)
Implementation Notes
By initializing the initial state as “straight (direction 1)”, we naturally allow transitions in any direction from row 1.
We use
NEG_INF = -10^{18}to distinguish unreachable states. Aval != NEG_INFcheck is included to prevent transitions from unreachable states.We save memory by reusing DP arrays with
swap(dp, ndp).When \(N = 1\), no movement occurs, so we handle this as a special case by directly outputting the maximum value in row 1.
Source Code
#include <bits/stdc++.h>
using namespace std;
int main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
int N, M;
cin >> N >> M;
const long long NEG_INF = -1e18;
vector<array<long long, 3>> dp(M, {NEG_INF, NEG_INF, NEG_INF});
vector<array<long long, 3>> ndp(M, {NEG_INF, NEG_INF, NEG_INF});
// Read and process row 0
for (int c = 0; c < M; c++) {
long long g;
cin >> g;
dp[c][1] = g;
}
if (N == 1) {
long long ans = NEG_INF;
for (int c = 0; c < M; c++) ans = max(ans, dp[c][1]);
cout << ans << endl;
return 0;
}
// Process rows 1 to N-1
for (int r = 1; r < N; r++) {
vector<long long> row(M);
for (int c = 0; c < M; c++) cin >> row[c];
for (int c = 0; c < M; c++) {
ndp[c] = {NEG_INF, NEG_INF, NEG_INF};
// ndp[c][0]: arrived by moving left (from c+1 in previous row)
// allowed after straight(1) or right(2)
if (c + 1 < M) {
long long val = max(dp[c+1][1], dp[c+1][2]);
if (val != NEG_INF)
ndp[c][0] = val + row[c];
}
// ndp[c][1]: arrived by moving straight (from c in previous row)
// allowed after any direction
{
long long val = max({dp[c][0], dp[c][1], dp[c][2]});
if (val != NEG_INF)
ndp[c][1] = val + row[c];
}
// ndp[c][2]: arrived by moving right (from c-1 in previous row)
// allowed after left(0) or straight(1)
if (c - 1 >= 0) {
long long val = max(dp[c-1][0], dp[c-1][1]);
if (val != NEG_INF)
ndp[c][2] = val + row[c];
}
}
swap(dp, ndp);
}
long long ans = NEG_INF;
for (int c = 0; c < M; c++) {
ans = max(ans, max({dp[c][0], dp[c][1], dp[c][2]}));
}
cout << ans << endl;
return 0;
}
This editorial was generated by claude4.6opus-thinking.