E - 工場の解体作業 / Factory Demolition Work Editorial by admin Editorial for printing
claude4.8opus-highOverview
This problem asks us to minimize the total demolition cost when we can freely choose the order in which we demolish \(N\) buildings. Demolishing a building changes the cost of other buildings that have not been demolished yet, so we need to carefully decide the demolition order. Given the small constraint \(N \leq 19\), we can solve this using Bitmask DP (Set DP).
Analysis
Key Observation: The demolition cost of a building depends only on the “set of already demolished buildings”
Let’s consider the actual cost of demolishing building \(j\). The initial cost is \(C_j\), but \(W_{i,j}\) is added for each building \(i\) that was demolished before \(j\). That is, if \(T\) is the set of buildings already demolished right before \(j\) is demolished, the cost to demolish \(j\) can be expressed as:
\[ C_j + \sum_{i \in T} W_{i,j} \]
The key point is that only “the contents of \(T\) (which buildings have been demolished first)” matters, and the specific order of demolition within \(T\) does not affect the cost of \(j\).
Issues with the Naive Approach
Trying all possible demolition orders would take \(N!\) ways. For \(N = 19\), \(19! \approx 1.2 \times 10^{17}\), which is far too slow to pass within the time limit.
Solution Strategy
From the observation above, if we maintain the “set of already demolished buildings \(S\)” as the state, we can compute the optimal cost without remembering the details of the exact order. Since \(N \leq 19\), there are only \(2^{19}\) possible sets, making DP with this state highly effective.
Algorithm
Representing the set as a bitmask, we define the following DP:
- \(\mathrm{dp}[S]\) = the minimum cost to demolish all buildings in the set \(S\)
For the transitions, we transition by considering “which building \(j\) in \(S\) was demolished last”. Let the set after removing \(j\) be \(\text{prev} = S \setminus \{j\}\). The set of demolished buildings immediately before \(j\) is demolished is exactly \(\text{prev}\), so the demolition cost of \(j\) is \(C_j + \sum_{i \in \text{prev}} W_{i,j}\). Therefore:
\[ \mathrm{dp}[S] = \min_{j \in S} \left( \mathrm{dp}[\text{prev}] + C_j + \sum_{i \in \text{prev}} W_{i,j} \right) \]
The initial value is \(\mathrm{dp}[\varnothing] = 0\), and the answer is \(\mathrm{dp}[2^N - 1]\), which represents demolishing all buildings.
Precomputing \(\sum_{i \in \text{prev}} W_{i,j}\)
Calculating this sum during each transition would be too slow, so we precompute:
\[ f_j[S] = \sum_{i \in S} W_{i,j} \]
We can compute this in \(O(2^N)\) for each \(j\) by extracting the least significant bit (LSB) \(l\) of the set \(S\) and updating as:
\[ f_j[S] = f_j[S \setminus \{l\}] + W_{l,j} \]
In the code, the index of the least significant set bit is obtained using (S & -S).bit_length() - 1.
Complexity
- Time Complexity: \(O(2^N \cdot N)\)
- The precomputation of \(f\) takes \(O(2^N \cdot N)\), and the DP itself also takes \(O(2^N \cdot N)\) because we iterate over the set bits for each \(S\). For \(N = 19\), the number of operations is around \(10^7\), which easily runs within the time limit.
- Space Complexity: \(O(2^N \cdot N)\)
- This is because we store \(f_j[S]\) as \(N\) arrays of length \(2^N\). The \(\mathrm{dp}\) array itself takes \(O(2^N)\) space.
Implementation Details
Extracting the Least Significant Bit: We extract only the least significant set bit using
b = t & -t, and find its index (building index) withb.bit_length() - 1. By looping while clearing the processed bit witht ^= b, we can efficiently iterate over all buildings \(j\) contained in \(S\).Negative Costs are Possible: Since \(C_i\) and \(W_{i,j}\) can be negative, the total cost can also be negative. However, we can handle this without issues by initializing with
float('inf')and taking the minimum. Python integers have arbitrary precision, so there is no need to worry about overflow.Direction of Transition: Since
prev = S ^ bis “the set \(S\) with \(j\) removed”, \(\mathrm{dp}[\text{prev}]\) represents a smaller set (with fewer set bits) than \(S\). Thus, if we iterate \(S\) in ascending order, \(\mathrm{dp}[\text{prev}]\) is guaranteed to be computed beforehand.Source Code
import sys
def main():
data = sys.stdin.buffer.read().split()
idx = 0
N = int(data[idx]); idx += 1
C = [int(data[idx + i]) for i in range(N)]; idx += N
W = []
for i in range(N):
row = [int(data[idx + j]) for j in range(N)]
idx += N
W.append(row)
size = 1 << N
# f[j][S] = sum over i in S of W[i][j]
f = []
for j in range(N):
col = [W[i][j] for i in range(N)]
fj = [0] * size
for S in range(1, size):
l = (S & -S).bit_length() - 1
fj[S] = fj[S ^ (1 << l)] + col[l]
f.append(fj)
INF = float('inf')
dp = [0] * size
for S in range(1, size):
best = INF
t = S
while t:
b = t & -t
j = b.bit_length() - 1
prev = S ^ b
v = dp[prev] + C[j] + f[j][prev]
if v < best:
best = v
t ^= b
dp[S] = best
print(dp[size - 1])
main()
This editorial was generated by claude4.8opus-high.
posted:
last update: