E - 山頂コレクション / Peak Collection 解説 by admin
Gemini 3.0 Flash (Thinking)Overview
This problem asks you to maximize the number of mountains to climb from a given set of \(N\) mountains, while satisfying the conditions: “altitudes are strictly increasing,” “total cost is at most \(B\),” and “the number of mountains is at most \(K\).”
It takes the form of a typical “Longest Increasing Subsequence (LIS)” problem with additional constraints on cost and count.
Analysis
Basic Approach
First, since we can only proceed in order of mountain indices, dynamic programming (DP) is effective. For a simple LIS, we would define “\(dp[i] = \) maximum count when mountain \(i\) is the last one selected,” but since we now have a total cost constraint, we need to either include the cost in the state or manage the minimum cost for each count.
DP State Definition
Looking at the constraints, \(N \leq 500, K \leq 50, B \leq 500\) — the values are relatively small overall. Therefore, we consider the following DP:
- \(dp[k][i] = \) minimum total cost when mountain \(i\) is the last one selected and a total of \(k\) mountains have been climbed
If for some \(k\), there exists at least one \(i\) such that \(dp[k][i] \leq B\), then we can determine that climbing \(k\) mountains is possible.
Transitions and Optimization
The transition for computing \(dp[k][i]\) is as follows: - \(dp[k][i] = \min \{ dp[k-1][j] \mid j < i, S_j < S_i \} + C_i\)
Computing this directly takes \(O(N^2)\) for each \(k\), resulting in \(O(K \cdot N^2)\) overall. With the given constraints (\(50 \times 500^2 = 1.25 \times 10^7\)), this requires some optimization in Python.
Therefore, we speed things up using a Fenwick Tree (Binary Indexed Tree, BIT). By coordinate-compressing the altitudes \(S_i\) into the range \(1 \sim N\), we can retrieve “the minimum cost among mountains with lower altitude” in \(O(\log N)\). This reduces the overall time complexity to \(O(K \cdot N \log N)\).
Algorithm
- Coordinate Compression: Since altitudes \(S_i\) can be as large as \(10^9\), we sort them and convert to ranks (\(1 \sim N\)). This makes them usable as BIT indices.
- Initialization: Compute the minimum cost for \(k=1\) (climbing only one mountain). For each mountain \(i\) satisfying \(C_i \leq B\), set \(dp[i] = C_i\).
- DP Update (for count \(k = 2\) to \(K\)):
For each \(k\), perform the following:
- Initialize the BIT.
- For mountains \(i = 1 \ldots N\) in order:
- Query the BIT for the “minimum cost of length \(k-1\)” in the range where altitude is less than \(S_i\).
- If (retrieved cost \(+ C_i\)) is within budget \(B\), set it as \(dp\_next[i]\).
- Add the previous step’s \(dp[i]\) (cost for length \(k-1\)) to the BIT.
- If \(dp\_next\) contains no valid values, it is impossible to increase the number of mountains further, so terminate.
- Output the Answer: Output the maximum \(k\) for which valid values existed.
Complexity
Time Complexity: \(O(K \cdot N \log N)\)
- \(O(N \log N)\) for coordinate compression.
- Within the \(K\) iterations of the loop, BIT operations (\(O(\log N)\)) are performed for each of the \(N\) elements.
- This results in approximately \(50 \times 500 \times \log_2(500) \approx 2.25 \times 10^5\) operations, which is sufficiently fast.
Space Complexity: \(O(N)\)
- Depends on the DP table (keeping only the previous \(k-1\) layer) and the BIT size.
Implementation Notes
Handling Minimum Values with BIT: A standard BIT computes “sums,” but here we implement it to manage “minimum values.” Initial values are set to infinity (
inf).Timing of Updates and Queries: When processing mountain \(i\), we first perform the query (searching for \(j < i\)), then perform the update (inserting ourselves into the BIT). This ensures we only consider mountains that come before the current one.
Early Termination: If no combination within the budget can be formed for a certain count \(k\), then \(k+1, k+2, \ldots\) are also impossible, so we can break out of the loop for efficiency.
Source Code
import sys
def solve():
# Read all input data at once for faster processing
try:
data = sys.stdin.read().split()
except EOFError:
return
if not data:
return
# N: Number of mountains, K: Maximum climbing limit, B: Budget
N = int(data[0])
K = int(data[1])
B = int(data[2])
C = [] # Entry fees
S = [] # Altitudes
for i in range(N):
C.append(int(data[3 + 2*i]))
S.append(int(data[4 + 2*i]))
# Coordinate compression for altitudes to map them to the range [1, N]
# Since all S_i are distinct as per the constraints, we can simply sort and rank them.
sorted_S = sorted(S)
rank = {val: i + 1 for i, val in enumerate(sorted_S)}
compressed_S = [rank[val] for val in S]
# dp[i] will store the minimum cost to climb 'k' mountains ending with mountain 'i'.
# We iterate through the possible sequence lengths k from 1 up to K.
# Initial case: k = 1 (sequences of length 1)
dp = [float('inf')] * N
found_any = False
for i in range(N):
if C[i] <= B:
dp[i] = C[i]
found_any = True
# If no single mountain can be climbed within the budget, the maximum m is 0.
if not found_any:
print(0)
return
max_m = 1
# If the limit K is 1, the maximum possible length is already found.
if K == 1:
print(1)
return
# Iterate for each sequence length k from 2 up to K.
for k in range(2, K + 1):
dp_next = [float('inf')] * N
# We use a Fenwick tree (BIT) to efficiently find the minimum cost
# among mountains j < i that satisfy the altitude condition S[j] < S[i].
bit = [float('inf')] * (N + 1)
found_any_k = False
for i in range(N):
# Step 1: Query the BIT for the minimum cost of a sequence of length k-1
# that ends at any mountain j < i with altitude S[j] < S[i].
# The rank of S[i] is compressed_S[i], so we query the range [1, rank-1].
res = float('inf')
curr_q = compressed_S[i] - 1
while curr_q > 0:
if bit[curr_q] < res:
res = bit[curr_q]
curr_q -= curr_q & (-curr_q)
# Step 2: If such a sequence exists and adding mountain i is within budget:
if res + C[i] <= B:
dp_next[i] = res + C[i]
found_any_k = True
# Step 3: Update the BIT with the minimum cost to reach mountain i with length k-1.
# This information will be available for mountains i' > i in this loop.
val = dp[i]
if val <= B:
curr_u = compressed_S[i]
while curr_u <= N:
if val < bit[curr_u]:
bit[curr_u] = val
curr_u += curr_u & (-curr_u)
# If we successfully formed at least one valid sequence of length k:
if found_any_k:
max_m = k
dp = dp_next
else:
# If no valid sequence of length k can be formed, no longer sequences are possible.
break
# Output the maximum number of mountains climbed.
print(max_m)
if __name__ == '__main__':
solve()
This editorial was generated by gemini-3-flash-thinking.
投稿日時:
最終更新: