公式

C - 隣接ペナルティ付き選択 / Selection with Adjacent Penalty 解説 by admin

gemini-3-flash-preview

Overview

This is a problem of selecting one or more jobs from \(N\) jobs and maximizing the profit, which is the total reward minus “the number of consecutively selected pairs \(\times K\)”.

Analysis

When deciding whether to “select” or “not select” each job \(i\), whether the penalty \(K\) is incurred depends on whether the previous job \(i-1\) was selected. Problems like this, where the previous state affects the current choice, can be efficiently solved using dynamic programming (DP).

Key Points

  1. State definition: When considering up to job \(i\), we need to maintain two states: “job \(i\) is selected” and “job \(i\) is not selected”.
  2. “Select at least one” constraint: Since all \(A_i\) are integers of at least 1, the final profit is guaranteed to be positive. By appropriately handling the initial state of “nothing selected yet” (initializing with a very small value \(-\infty\)), this constraint is naturally satisfied.
  3. Transition logic:
    • If job \(i\) is not selected: Carry over the maximum profit so far, regardless of whether job \(i-1\) was selected or not.
    • If job \(i\) is selected:
      • Job \(i\) is the first job selected (profit: \(A_i\))
      • Job \(i-1\) was not selected, and job \(i\) is selected (profit: \(dp[i-1][\text{not selected}] + A_i\))
      • Job \(i-1\) was also selected, and job \(i\) is also selected (profit: \(dp[i-1][\text{selected}] + A_i - K\))

Algorithm

We perform DP with the following two states:

  • dp0: The maximum profit among jobs \(1 \dots i\) when job \(i\) is not selected
  • dp1: The maximum profit among jobs \(1 \dots i\) when job \(i\) is selected

Transition Equations

For each \(i = 1, \dots, N-1\), letting the values from the previous step be prev_dp0 and prev_dp1:

  1. dp0 = max(prev_dp0, prev_dp1)
    • Since job \(i\) is not selected, no penalty is incurred. We simply take the maximum up to \(i-1\).
  2. dp1 = max(A[i], prev_dp0 + A[i], prev_dp1 + A[i] - K)
    • A[i]: Selecting job \(i\) as the very first job
    • prev_dp0 + A[i]: The previous job was not selected, and we select the current one
    • prev_dp1 + A[i] - K: The previous job was also selected, and we select the current one too (penalty \(K\) is incurred)

The answer is max(dp0, dp1) at the end.

Complexity

  • Time complexity: \(O(N)\)
    • We scan through \(N\) jobs once, performing constant-time computation at each step.
  • Space complexity: \(O(N)\)
    • \(O(N)\) is required to store the input array \(A\). The DP transition itself can be implemented with \(O(1)\) additional space (variables only).

Implementation Notes

  • Initial values: For the first job (\(i=0\)), dp0 is set to \(-\infty\) to indicate “nothing has been selected yet”, and dp1 is set to A[0].

  • Fast I/O: Since \(N\) can be as large as \(2 \times 10^5\), in Python, reading all input at once using sys.stdin.read().split() can reduce execution time.

  • Penalty handling: When \(m\) consecutive jobs are selected, the number of pairs is \(m-1\), so the transition prev_dp1 + A[i] - K correctly computes the penalty.

    Source Code

import sys

def solve():
    # Fast I/O: read all input at once and split into tokens
    # This is more efficient for large inputs in competitive programming.
    input_data = sys.stdin.read().split()
    if not input_data:
        return
    
    # N: number of jobs, K: penalty per continuous pair
    N = int(input_data[0])
    K = int(input_data[1])
    
    # A: rewards for each job A_1, A_2, ..., A_N
    # list(map(int, ...)) is efficient for converting tokens to integers.
    A = list(map(int, input_data[2:]))
    
    # dp0: Maximum profit considering jobs 1 to i, where job i is NOT selected.
    #      We use -inf to ensure that the "at least one job" requirement is handled.
    # dp1: Maximum profit considering jobs 1 to i, where job i IS selected.
    
    # Base case: the first job (index 0)
    # Since A_i >= 1, the maximum profit will always be positive.
    inf = 10**18
    dp0 = -inf
    dp1 = A[0]
    
    # DP transitions for jobs i = 1 to N-1
    for i in range(1, N):
        # Store values from the previous step (job i-1)
        prev_dp0 = dp0
        prev_dp1 = dp1
        
        # Transition for not selecting job i:
        # We take the maximum profit achievable from the first i-1 jobs.
        dp0 = max(prev_dp0, prev_dp1)
        
        # Transition for selecting job i:
        # There are three sub-cases for selecting job i:
        # 1. Job i is the first job selected in the sequence.
        # 2. Job i is selected, and some job(s) from the first i-1 were selected, 
        #    but job i-1 was NOT selected.
        # 3. Job i is selected, and job i-1 WAS selected (incurring a penalty K).
        
        # Combining Case 1 and Case 2: A[i] + max(0, prev_dp0)
        # Note: Since A_i >= 1, finite prev_dp0 is always >= 1.
        
        dp1 = max(A[i], prev_dp0 + A[i], prev_dp1 + A[i] - K)
        
    # The final answer is the maximum of the two states after considering all N jobs.
    print(max(dp0, dp1))

if __name__ == '__main__':
    solve()

This editorial was generated by gemini-3-flash-preview.

投稿日時:
最終更新: