公式

D - 冒険者の装備選び / Adventurer's Equipment Selection 解説 by admin

Claude 4.6 Opus (Thinking)

Overview

Given \(N\) pieces of equipment, select one or more such that the total defense is at least \(K\), and maximize the total attack power. This can be solved as a variant of the 0-1 knapsack problem with constraints.

Analysis

Naive Approach (Exhaustive Search)

Since each of the \(N\) pieces of equipment has 2 choices (“select” or “don’t select”), an exhaustive search would require examining \(2^N\) combinations. When \(N \leq 100\), \(2^{100}\) is an astronomically large number, making this approach completely infeasible.

Key Insight

This problem is a variant of the 0-1 knapsack problem. The standard knapsack problem has the constraint “total weight is at most \(K\)”, but in this problem the constraint is “total defense is at least \(K\)”.

The key observation here is that there is no need to distinguish defense values that exceed \(K\). Whether the total defense is \(K\) or \(K+100\), the condition “at least \(K\)” is equally satisfied. Therefore, we can restrict the defense values in our DP states to the range \(0\) through \(K\), merging all values \(K\) or above into a single state \(K\).

Algorithm

We use a 0-1 knapsack DP.

  • State: \(dp[w]\) = the maximum total attack power achievable by selecting equipment whose total defense is exactly \(w\) (where \(w = K\) represents “defense of \(K\) or more”)
  • Initial state: \(dp[0] = 0\), all others are \(-1\) (unreachable)
  • Transition: For each piece of equipment \((W_i, S_i)\), for every \(w\) where \(dp[w]\) is valid, consider the transition to a new defense value \(\min(w + W_i, K)\): $\(dp'[\min(w + W_i, K)] = \max(dp'[\min(w + W_i, K)],\ dp[w] + S_i)\)$

By using \(\min(w + W_i, K)\), all defense values of \(K\) or more are consolidated into index \(K\).

  • Since each piece of equipment can be used at most once, we update using a copy of the original \(dp\) array (new_dp = dp[:]) during transitions.

Finally, if \(dp[K]\) is not \(-1\), it is the answer; if it is \(-1\), then no valid selection exists.

Concrete Example

For \(N=3, K=5\) with equipment \((W, S) = (3, 10), (2, 8), (4, 15)\): - Selecting equipment 1 and 2: defense \(3+2=5 \geq 5\), attack \(10+8=18\) - Selecting equipment 1 and 3: defense \(3+4=7 \geq 5\), attack \(10+15=25\) - Selecting equipment 2 and 3: defense \(2+4=6 \geq 5\), attack \(8+15=23\)

Therefore the answer is \(25\).

Complexity

  • Time complexity: \(O(N \times K)\)
    • For each of the \(N\) pieces of equipment, we scan states from \(0\) to \(K\)
  • Space complexity: \(O(K)\)
    • The size of the DP table

Since \(N \leq 100\) and \(K \leq 10^4\), the maximum number of operations is approximately \(100 \times 10^4 = 10^6\), which is sufficiently fast.

Implementation Notes

  • Clamping defense to the upper bound: By using nw = min(w + W, K), all defense values of \(K\) or more are consolidated into index \(K\). This is the most important point of this problem.

  • 0-1 knapsack update method: To ensure each piece of equipment is used at most once, we create a copy with new_dp = dp[:] before updating. In a standard 0-1 knapsack, this is typically handled by iterating in reverse order, but in this problem, since the transition destination min(w+W, K) can cause multiple values of \(w\) to map to the same index, using a copy is safer.

  • Managing unreachable states: We treat \(-1\) as “unreached” and skip transitions from states where the source is \(-1\).

    Source Code

import sys

def solve():
    input_data = sys.stdin.read().split()
    idx = 0
    N = int(input_data[idx]); idx += 1
    K = int(input_data[idx]); idx += 1
    
    items = []
    for i in range(N):
        W = int(input_data[idx]); idx += 1
        S = int(input_data[idx]); idx += 1
        items.append((W, S))
    
    # dp[w] = maximum attack power achievable with exactly defense w
    # We cap w at K since any defense >= K is equivalent for our purposes
    # dp[w] for w = 0..K, where dp[K] represents "defense >= K"
    
    NEG_INF = -1
    dp = [NEG_INF] * (K + 1)
    dp[0] = 0
    
    for W, S in items:
        # Traverse in reverse to ensure each item is used at most once
        new_dp = dp[:]
        for w in range(K + 1):
            if dp[w] == NEG_INF:
                continue
            nw = min(w + W, K)
            if new_dp[nw] < dp[w] + S:
                new_dp[nw] = dp[w] + S
        dp = new_dp
    
    if dp[K] == NEG_INF:
        print(-1)
    else:
        print(dp[K])

solve()

This editorial was generated by claude4.6opus-thinking.

投稿日時:
最終更新: