E - 展示会の出展計画 / Exhibition Booth Planning Editorial by admin
gemini-3.1-pro-thinkingOverview
This is a variation of the knapsack problem where, while satisfying the weight limit \(W\), we maximize the value obtained by subtracting “the square of the number of distinct venues \(c\) selected” from “the total profit of selected exhibits.”
Analysis
This problem takes the form of a standard knapsack problem with an added “penalty (delivery cost) depending on the number of distinct venues of selected exhibits.”
First, let’s focus on the constraint \(K \le 8\) (the number of venues is very small). If we perform a standard DP where we look at exhibits one by one and decide whether to “select or not select” each one, we need to remember “which venues’ exhibits have been selected.” If we maintain this as a set (bitmask), the number of states becomes \(2^K \times W\), and the time complexity becomes \(O(N \cdot 2^K \cdot W)\). While this is solvable, it may hit the time limit (TLE) in languages like Python.
Therefore, we consider grouping and processing exhibits “by venue.” If we process venue by venue, deciding either “select nothing from this venue” or “select one or more from this venue,” we don’t need to remember the specific combination of venues. We only need to maintain “how many venues we’ve selected from so far (the count \(c\))” as a state. This reduces the number of states to \(K \times W\), allowing for a faster solution.
Algorithm
Grouping Group all exhibits by their respective venues.
DP Table Definition Define \(dp[c][w]\) as “the maximum profit when selecting exhibits from a total of \(c\) distinct venues with a total weight of \(w\).” The initial state is \(dp[0][0] = 0\), and all other values are set to a very small value (\(-\infty\)).
DP Transitions Update the DP table by processing each venue in order. When looking at the exhibits of a certain venue, consider the following two cases:
- When selecting nothing from this venue: The state from the previous venues is carried over as is.
- When selecting one or more from this venue: The number of distinct venues \(c\) increases by \(1\). Therefore, using the state \(dp[c-1]\) from the previous venues as a base, perform a knapsack DP using only the exhibits from the current venue.
Computing the Answer After processing all venues, \(dp[c][w]\) contains the maximum profit for each condition. For all combinations of \(c\) (\(0 \le c \le K\)) and \(w\) (\(0 \le w \le W\)), compute \(dp[c][w] - c^2\), and the maximum among these is the answer.
Complexity
- Time complexity: \(O(N \cdot K \cdot W)\) For each of the \(N\) exhibits, we iterate over the number of venue types \(c\) (at most \(K\)) and weight \(w\) (at most \(W\)), so the overall complexity is \(O(N K W)\).
- Space complexity: \(O(K \cdot W)\) We maintain a 2D array for the DP table with venue count \(K\) and weight \(W\), resulting in \(O(K W)\).
Implementation Details
Some care is needed when handling the case of “selecting multiple exhibits from the same venue.”
In the code, an array temp_c is prepared to manage the “state where at least one exhibit has been selected from the current venue.”
When selecting an exhibit from the current venue (weight \(w_i\), profit \(v_i\)), there are two possible source states (states before selection):
1. When selecting an exhibit from this venue for the first time:
Add \(v_i\) to dp[c-1][w - w_i], which is the state from the previous venues.
2. When other exhibits from this venue have already been selected:
Add \(v_i\) to temp_c[w - w_i], which is the intermediate state for the current venue.
By taking the larger of these two and updating temp_c[w], we can correctly satisfy the condition “select one or more from a single venue” while processing it the same way as a standard knapsack problem. Additionally, to avoid selecting the same exhibit multiple times, the weight \(w\) loop must be iterated in reverse order (from \(W\) down to \(w_i\)).
Source Code
import sys
def solve():
input = sys.stdin.read
data = input().split()
if not data:
return
N = int(data[0])
K = int(data[1])
W = int(data[2])
if N == 0:
print(0)
return
items = []
idx = 3
for _ in range(N):
w = int(data[idx])
v = int(data[idx+1])
d = int(data[idx+2])
items.append((w, v, d))
idx += 3
venues = list(set(d for w, v, d in items))
M = len(venues)
venue_to_id = {v: i for i, v in enumerate(venues)}
items_by_venue = [[] for _ in range(M)]
for w, v, d in items:
items_by_venue[venue_to_id[d]].append((w, v))
INF = 10**15
# dp[c][w]: c個の異なる会場の展示品を選び、重さの合計がwのときの最大利益
dp = [[-INF] * (W + 1) for _ in range(M + 1)]
dp[0][0] = 0
for k in range(M):
next_dp = [row[:] for row in dp]
for c in range(1, k + 2):
temp_c = [-INF] * (W + 1)
dp_prev = dp[c - 1]
for w_i, v_i in items_by_venue[k]:
for w in range(W, w_i - 1, -1):
prev_w = w - w_i
val1 = temp_c[prev_w]
val2 = dp_prev[prev_w]
m = val1 if val1 > val2 else val2
if m != -INF:
m += v_i
if m > temp_c[w]:
temp_c[w] = m
next_dp_c = next_dp[c]
for w in range(W + 1):
if temp_c[w] > next_dp_c[w]:
next_dp_c[w] = temp_c[w]
dp = next_dp
ans = 0
for c in range(M + 1):
cost = c * c
max_v = max(dp[c])
if max_v - cost > ans:
ans = max_v - cost
print(ans)
if __name__ == '__main__':
solve()
This editorial was generated by gemini-3.1-pro-thinking.