C - 買い物の最小品数 / Minimum Number of Items to Buy Editorial by admin Editorial for printing
Claude 4.6 Opus (Thinking)Overview
Given \(N\) items, we want to select some of them so that the total price is exactly \(S\) yen. If such a selection exists, find the minimum number of items; otherwise, output \(-1\). This can be solved using dynamic programming (DP) as a variant of the 0/1 knapsack problem.
Analysis
Naive Approach (Exhaustive Search)
Since each of the \(N\) items can either be “selected” or “not selected,” an exhaustive search would examine \(2^N\) combinations. When \(N = 100\), this gives \(2^{100} \approx 10^{30}\), which is far too slow.
Key Insight
This problem requires us to simultaneously determine “whether the total amount can be exactly \(S\)” and “if so, what is the minimum number of items.” The important observations are:
- Each item can be selected at most once (0/1 knapsack)
- The target amount \(S\) is at most \(10000\), which is relatively small
This allows us to efficiently use DP with the amount as the state.
DP Definition
Define \(dp[j]\) = “the minimum number of items selected such that the total price is exactly \(j\) yen.”
The initial values are \(dp[0] = 0\) (selecting nothing gives a total of \(0\) yen with \(0\) items), and all others are \(\infty\) (not yet achievable).
Transition
When considering item \(i\) (with price \(A_i\)), for each amount \(j\):
\[dp[j] = \min(dp[j],\ dp[j - A_i] + 1)\]
This means “take a way to make \(j - A_i\) yen and add item \(i\) to make \(j\) yen.”
Reason for Reverse-Order Loop
To avoid using the same item more than once, we loop \(j\) in reverse order from \(S\) down to \(A_i\). If we looped in ascending order, we would use an already-updated \(dp[j - A_i]\), effectively selecting the same item multiple times.
Algorithm
- Initialize \(dp[0] = 0\) and \(dp[1], dp[2], \ldots, dp[S] = \infty\).
- For each item \(i = 0, 1, \ldots, N-1\):
- Loop \(j = S, S-1, \ldots, A_i\) in that order, updating \(dp[j] = \min(dp[j],\ dp[j - A_i] + 1)\).
- At the end, if \(dp[S]\) is still \(\infty\), output \(-1\); otherwise, output \(dp[S]\).
Worked Example
For \(N = 3,\ S = 7,\ A = [3, 4, 5]\):
- Initial: \(dp = [0, \infty, \infty, \infty, \infty, \infty, \infty, \infty]\)
- After processing item 1 (price 3): \(dp[3] = 1\)
- After processing item 2 (price 4): \(dp[4] = 1,\ dp[7] = 2\)
- After processing item 3 (price 5): \(dp[5] = 1,\ dp[8]\) is out of range so no change
Result: \(dp[7] = 2\) (select items 1 and 2)
Complexity
- Time complexity: \(O(N \times S)\) — for each item, we loop up to amount \(S\)
- Space complexity: \(O(S)\) — only a 1-dimensional DP array is used
Since \(N \leq 100,\ S \leq 10000\), the maximum number of operations is about \(100 \times 10000 = 10^6\), which runs sufficiently fast.
Implementation Notes
Forgetting the reverse-order loop will cause the same item to be used multiple times (turning it into an unbounded knapsack problem), so be careful.
Using
float('inf')as the initial DP value naturally represents the state “this amount has not been achieved yet.”At the end, checking whether \(dp[S]\) is \(\infty\) simultaneously handles both the feasibility check and the output of the minimum number of items.
Source Code
def solve():
N, S = map(int, input().split())
A = list(map(int, input().split()))
# dp[j] = minimum number of items to make sum exactly j
INF = float('inf')
dp = [INF] * (S + 1)
dp[0] = 0
for i in range(N):
# Traverse in reverse to ensure each item is used at most once (0/1 knapsack)
for j in range(S, A[i] - 1, -1):
if dp[j - A[i]] + 1 < dp[j]:
dp[j] = dp[j - A[i]] + 1
print(dp[S] if dp[S] != INF else -1)
solve()
This editorial was generated by claude4.6opus-thinking.
posted:
last update: