C - 隣接ペナルティ付き選択 / Selection with Adjacent Penalty Editorial by admin
Claude 4.5 OpusOverview
This problem asks you to select one or more jobs from \(N\) jobs and maximize profit (total reward − total penalty), where selecting both jobs with consecutive numbers incurs a penalty of \(K\).
Analysis
Key Insight
In this problem, the decision of whether to accept job \(i\) depends only on whether you accepted the previous job \(i-1\). This is because penalties only occur for “pairs of consecutive numbers.”
Problem with Naive Approach
Enumerating all ways to select jobs gives \(2^N\) possibilities, which is far too slow for \(N \leq 2 \times 10^5\) (TLE).
Solution
We use Dynamic Programming (DP). By managing two states for each job—”accept/don’t accept”—and computing the current state from the previous job’s state, we can solve this efficiently.
Algorithm
State Definition
When considering up to job \(i\), we manage the following three states:
prev_not_take_none: Maximum profit when not selecting job \(i\) and having selected no jobs yetprev_not_take_some: Maximum profit when not selecting job \(i\) and having selected one or more jobsprev_take: Maximum profit when selecting job \(i\) (necessarily having selected at least one job)
Transitions
For job \(i\):
When not accepting job \(i\):
- curr_not_take_none = prev_not_take_none (maintain the state of having selected nothing)
- curr_not_take_some = max(prev_not_take_some, prev_take) (inherit the state of having selected something before)
When accepting job \(i\):
- curr_take = max(prev_not_take_none + A[i], prev_not_take_some + A[i], prev_take + A[i] - K)
- If the previous job was not selected: gain \(A_i\) without penalty
- If the previous job was selected: gain \(A_i\) while paying penalty \(K\)
Concrete Example
For \(N=3, K=5, A=[10, 3, 8]\):
| Job | not_take_none | not_take_some | take |
|---|---|---|---|
| Initial | 0 | \(-\infty\) | 10 |
| i=1 | 0 | 10 | max(0+3, \(-\infty\)+3, 10+3-5) = 8 |
| i=2 | 0 | 10 | max(0+8, 10+8, 8+8-5) = 18 |
Answer: \(\max(10, 18) = 18\) (selecting jobs 1 and 3)
Complexity
- Time complexity: \(O(N)\) (constant time processing for each job)
- Space complexity: \(O(1)\) (only storing the previous three state values)
Implementation Notes
Handling the “select at least one” constraint: A simple DP would include the state of selecting nothing, so we need to separately manage “the state of having selected nothing yet” and “the state of having selected one or more.”
Setting initial values: By setting the initial value of
prev_not_take_someto \(-\infty\), we express that initially the state “having selected one or more but not selecting the current job” does not exist.Space optimization: Instead of maintaining the entire array, we manage only the previous state with variables, keeping space complexity at \(O(1)\).
Source Code
def solve():
N, K = map(int, input().split())
A = list(map(int, input().split()))
# dp[i][j] = maximum profit when considering up to job i
# j = 0: do not accept job i
# j = 1: accept job i
# Initial state: start from selecting nothing
# However, we ultimately need to select at least one
INF = float('inf')
# dp[0]: maximum profit when not accepting job i
# dp[1]: maximum profit when accepting job i
# Initially nothing is selected
# prev_not_take: maximum profit when previous job was not accepted
# prev_take: maximum profit when previous job was accepted
prev_not_take = 0 # nothing selected
prev_take = A[0] # select job 0
for i in range(1, N):
# When not accepting job i
# Either previous state is fine
curr_not_take = max(prev_not_take, prev_take)
# When accepting job i
# If previous job was not accepted: no additional cost
# If previous job was accepted: additional cost K
curr_take = max(prev_not_take + A[i], prev_take + A[i] - K)
prev_not_take = curr_not_take
prev_take = curr_take
# We need to select at least one in the end
# prev_take has at least one selected
# prev_not_take might have 0 selected
# Find maximum when at least one is selected
# prev_take always has at least one selected
# Need to consider cases where prev_not_take has at least one selected
# Redo the DP
# dp[i][j][k]: considered up to i-th, j=select job i or not, k=selected at least one or not
# prev_not_take_none: did not select previous job, haven't selected anything yet
# prev_not_take_some: did not select previous job, selected at least one
# prev_take: selected previous job (necessarily selected at least one)
prev_not_take_none = 0
prev_not_take_some = -INF
prev_take = A[0]
for i in range(1, N):
# When not accepting job i
curr_not_take_none = prev_not_take_none
curr_not_take_some = max(prev_not_take_some, prev_take)
# When accepting job i
curr_take = max(prev_not_take_none + A[i], prev_not_take_some + A[i], prev_take + A[i] - K)
prev_not_take_none = curr_not_take_none
prev_not_take_some = curr_not_take_some
prev_take = curr_take
# Maximum value among states with at least one selected
ans = max(prev_not_take_some, prev_take)
print(ans)
solve()
This editorial was generated by claude4.5opus.
posted:
last update: