公式

D - カード取りゲーム / Card Taking Game 解説 by admin

gemini-3.5-flash-thinking

Overview

This problem asks you to find the final score of a game where two players each act optimally. By leveraging the property that the remaining cards always form a contiguous subarray, we can solve this using interval DP (dynamic programming).

Analysis

Properties of the Game and Approach

In games where two players act optimally with respect to their own objectives (two-player zero-sum finite deterministic perfect information games), backward induction (minimax method) is effective — reasoning from the final state (end of game) backward to determine what happens when both players play optimally.

Moreover, the operation of removing a card can only be performed from the “left end” or the “right end.” Therefore, at any point during the game, the remaining cards always form a contiguous subarray of the original sequence.

Thus, we define the game state as “the state where cards from the \(i\)-th to the \(j\)-th (from the left) remain,” and consider an interval DP that manages the score obtainable from this state until the end of the game.

Determining Whose Turn It Is

Let \(len\) be the number of remaining cards. Since the total number of cards at the start of the game is \(N\), the number of cards removed so far is \(N - len\). Since the first player (Takahashi) and second player alternate removing one card at a time, we can determine whose turn it is based on the number of cards removed so far.

  • When \((N - len) \pmod 2 == 0\): Takahashi’s turn
    • Takahashi wants to maximize the score, so he chooses the transition that yields a larger value (\(\max\)).
  • When \((N - len) \pmod 2 \neq 0\): Aoki’s turn
    • Aoki wants to minimize the score, so he chooses the transition that yields a smaller value (\(\min\)).

Memory Optimization (Reduction to 1D DP)

If we naively maintain a 2D array dp[l][r] (the score when the interval \([l, r]\) remains), the space complexity becomes \(O(N^2)\). When \(N = 3000\), this may exceed the memory limit.

However, to compute states of length \(len\), we only need to know the states of length \(len - 1\). Therefore, by updating the array in order of increasing interval length (from \(1\) to \(N\)), we can reduce the stored data to a single 1D array dp (space complexity \(O(N)\)).


Algorithm

  1. Initialization:
    • When length = 1, only one card remains. The score in this case is the number written on the card itself, so we initialize the DP array as dp = A.
  2. Transitions:
    • We update the DP array by incrementing length from \(2\) to \(N\) by \(1\).
    • From an interval \([i, i + \text{length} - 1]\) of length length, the two reachable states are:
      • If the left end is removed: transition to the interval \([i + 1, i + \text{length} - 1]\) of length length - 1.
      • If the right end is removed: transition to the interval \([i, i + \text{length} - 2]\) of length length - 1.
    • Depending on whose turn it is, the next state is determined as follows:
      • Takahashi’s turn: \(\max(\text{result of removing left end}, \text{result of removing right end})\)
      • Aoki’s turn: \(\min(\text{result of removing left end}, \text{result of removing right end})\)
  3. Output the answer:
    • Once the update is complete up to length = N, dp stores the score for the interval of length \(N\) (i.e., the initial state where all cards remain). The answer is dp[0].

Complexity

  • Time complexity: \(O(N^2)\)
    • The outer loop (interval length \(len\)) runs \(N\) times, and the inner loop (starting position \(i\) of each interval) runs at most \(N\) times, so the overall complexity is \(O(N^2)\). When \(N = 3000\), the number of operations inside the loop is approximately \(4.5 \times 10^6\), which is well within the time limit.
  • Space complexity: \(O(N)\)
    • Since we only maintain a single 1D array of size \(N\), the memory usage is kept extremely small.

Implementation Notes

  • Smart transitions using the zip function:

    • Each element of dp represents the result for an interval of length length - 1.
    • By using zip(dp, dp[1:]), we can easily create pairs of two adjacent intervals (“result of removing the right end” and “result of removing the left end”) and compute the results for the next length length all at once.
  • Optimization in Python:

    • In Python, calling the built-in max(x, y) or min(x, y) functions many times inside a loop becomes slow due to function call overhead.

    • In the code, we use ternary expressions like x if x > y else y to improve execution speed.

      Source Code

import sys

def solve():
    input = sys.stdin.read
    data = input().split()
    if not data:
        return
    N = int(data[0])
    A = [int(x) for x in data[1:]]
    
    dp = A
    for length in range(2, N + 1):
        if (N - length) % 2 == 0:
            dp = [x if x > y else y for x, y in zip(dp, dp[1:])]
        else:
            dp = [x if x < y else y for x, y in zip(dp, dp[1:])]
            
    print(dp[0])

if __name__ == '__main__':
    solve()

This editorial was generated by gemini-3.5-flash-thinking.

投稿日時:
最終更新: