D - 果樹園の収穫 / Orchard Harvest 解説 by admin
Claude 4.6 Opus (Thinking)Overview
This is a problem where we harvest a total of at most \(M\) times from \(N\) types of fruit trees, maximizing the total number of fruits obtained. Since each tree yields less fruit with each successive harvest, the optimal strategy is to greedily harvest from “the tree that yields the most fruit” each time.
Analysis
Key Insight: Greedy is Optimal
Each harvest can be considered independently as “which tree yields the most fruit at this moment?” This is because the number of fruits obtained from the \(k\)-th harvest of tree \(i\) is determined as \(\max(F_i - (k-1) \times D_i, \ 0)\), and changing the order of harvests does not change the sequence of yields from each tree.
For example, if tree A’s harvest yields are \([10, 7, 4, 1]\) and tree B’s harvest yields are \([8, 3]\), then for a total of \(M = 3\) harvests, the optimal choice is to pick the top 3 values \(10, 8, 7\) from all candidates \(\{10, 8, 7, 4, 3, 1\}\) in descending order.
Problem with the Naive Approach
The method of enumerating and sorting all harvest candidates from all trees is infeasible in both memory and time, since the number of possible harvests per tree can be extremely large (\(F_i / D_i\) can be up to \(10^9\)).
Solution: Priority Queue (Heap)
Instead of enumerating all candidates, we manage only the “yield of the next harvest” for each tree using a heap. After extracting the maximum value, we push the next harvest yield for that tree back into the heap, repeating this operation at most \(M\) times. This allows us to efficiently process only what is needed.
Algorithm
- Initialization: For each tree \(i\), if the first harvest yield \(F_i > 0\), insert \(F_i\) into a max-heap (in Python, since the heap is a min-heap, insert \(-F_i\)).
- Greedily harvest \(M\) times:
- Extract the maximum value \(v\) from the heap.
- If \(v \leq 0\), there is no benefit in harvesting further, so stop.
- Add \(v\) to the total.
- If the next harvest yield \(v - D_i\) is positive, push it back into the heap.
- Output the total.
Concrete Example
For \(N=2, M=3\), tree 1: \((F_1, D_1) = (10, 3)\), tree 2: \((F_2, D_2) = (8, 5)\):
| Harvest # | Heap State | Selected | Next Value |
|---|---|---|---|
| 1st | \(\{10, 8\}\) | 10 (tree 1) | \(10 - 3 = 7\) |
| 2nd | \(\{8, 7\}\) | 8 (tree 2) | \(8 - 5 = 3\) |
| 3rd | \(\{7, 3\}\) | 7 (tree 1) | \(7 - 3 = 4\) |
Total: \(10 + 8 + 7 = 25\)
Complexity
- Time Complexity: \(O((N + M) \log N)\)
- \(O(N \log N)\) for initial heap construction, \(O(M \log N)\) for at most \(M\) heap operations.
- Space Complexity: \(O(N)\)
- The heap contains at most \(N\) entries, one “next harvest yield” per tree.
Implementation Notes
Python’s heap is a min-heap, so we store negated values to use it as a max-heap.
Trees whose harvest yield drops to \(0\) or below are not pushed back into the heap, avoiding unnecessary operations.
If the heap becomes empty (all trees’ harvest yields have reached \(0\)), the loop terminates even before reaching \(M\) iterations.
Input is read all at once using
sys.stdin.buffer.read()to ensure speed when handling large inputs.Source Code
import heapq
def solve():
import sys
input_data = sys.stdin.buffer.read().split()
idx = 0
N = int(input_data[idx]); idx += 1
M = int(input_data[idx]); idx += 1
# Use a max-heap (negate values for Python's min-heap)
# Each entry: (-current_yield, F_i, D_i, k) where k is the next harvest number (1-indexed)
# yield for k-th harvest = max(F_i - (k-1)*D_i, 0)
heap = []
for i in range(N):
F = int(input_data[idx]); idx += 1
D = int(input_data[idx]); idx += 1
if F > 0:
heapq.heappush(heap, (-F, F, D))
total = 0
for _ in range(M):
if not heap:
break
neg_val, F, D = heapq.heappop(heap)
val = -neg_val
if val <= 0:
break
total += val
# Next harvest from this tree: val - D
next_val = val - D
if next_val > 0:
heapq.heappush(heap, (-next_val, F, D))
print(total)
solve()
This editorial was generated by claude4.6opus-thinking.
投稿日時:
最終更新: