結果

問題 No.952 危険な火薬庫
ユーザー qwewe
提出日時 2025-05-14 13:17:43
言語 C++17
(gcc 13.3.0 + boost 1.87.0)
結果
TLE  
実行時間 -
コード長 6,370 bytes
コンパイル時間 761 ms
コンパイル使用メモリ 81,708 KB
実行使用メモリ 7,844 KB
最終ジャッジ日時 2025-05-14 13:18:52
合計ジャッジ時間 4,442 ms
ジャッジサーバーID
(参考情報)
judge5 / judge2
このコードへのチャレンジ
(要ログイン)
ファイルパターン 結果
sample AC * 3
other TLE * 1 -- * 22
権限があれば一括ダウンロードができます

ソースコード

diff #

#include <iostream>
#include <vector>
#include <numeric>
#include <algorithm>

using namespace std;

// Use long long for costs and sums to avoid overflow, as intermediate sums and squares can be large.
using ll = long long;

// Define a large constant to represent infinity.
// The maximum possible cost could be roughly (N * max(A_i))^2.
// For N=3000 and max(A_i)=100000, this is (3000 * 100000)^2 = (3e8)^2 = 9e16.
// This fits within a 64-bit signed long long (which goes up to approx 9e18).
// Using 4e18 as INF provides a safe margin.
const ll INF = 4e18; 

int main() {
    // Use fast I/O operations.
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);

    int N; // Number of doors
    cin >> N;

    vector<ll> A(N); // Danger values for each door
    for (int i = 0; i < N; ++i) {
        cin >> A[i];
    }

    // Calculate prefix sums of danger values.
    // S[k] will store the sum of danger values for doors 1 through k.
    // S[0] is 0. S has size N+1.
    vector<ll> S(N + 1, 0);
    for (int i = 0; i < N; ++i) {
        // A[i] corresponds to door i+1. S[i+1] stores sum up to door i+1.
        S[i + 1] = S[i] + A[i]; 
    }

    // Initialize the DP table.
    // dp[i][j] will store the minimum total danger cost using exactly j open doors
    // among the first i doors (doors numbered 1 to i).
    // The table dimensions are (N+1) x (N+1).
    // dp[i] row corresponds to the state after considering door i.
    vector<vector<ll>> dp(N + 1, vector<ll>(N + 1, INF));

    // Base case: Using 0 open doors always results in a total danger cost of 0.
    // This is true for any prefix length i, including the empty prefix (i=0).
    for (int i = 0; i <= N; ++i) {
        dp[i][0] = 0;
    }

    // Fill the DP table using dynamic programming.
    // Iterate through each door index i from 1 to N.
    for (int i = 1; i <= N; ++i) {
        // Iterate through the number of open doors j required, from 1 up to i.
        // It's impossible to open more doors than available, so j <= i.
        for (int j = 1; j <= i; ++j) {
            
            // Option 1: Door i is kept closed.
            // In this case, the minimum cost is the same as the minimum cost using j open doors
            // among the first i-1 doors. We inherit the value from dp[i-1][j].
            dp[i][j] = dp[i - 1][j];

            // Option 2: Door i is opened.
            // If door i is open, it must be the rightmost door of some connected component [L, i].
            // We need to consider all possible start positions L for this component.
            // The length of this component is k = i - L + 1.
            // Iterate over possible lengths k for this component ending at i.
            // The length k must be at least 1 and cannot exceed the total number of open doors j.
            for (int k = 1; k <= j; ++k) { 
                int L = i - k + 1; // Calculate the start index L (1-based) of the component.
                
                // The component [L, i] uses k doors. The remaining j-k doors must be chosen
                // from the prefix of doors 1 to L-2. This is because door L-1 must be closed
                // (or not exist if L=1) to ensure [L, i] is a maximal component starting at L.
                int prev_doors_count = j - k; // Number of doors needed from the prefix 1..(L-2).
                
                // The state in the DP table that corresponds to the prefix 1..(L-2) is indexed by L-2.
                // dp[prev_prefix_idx][prev_doors_count] gives the minimum cost for that subproblem.
                int prev_prefix_idx = L - 2; 

                ll prev_cost; // Stores the minimum cost from the subproblem dp[L-2][j-k].
                
                // Determine the cost from the prefix 1..(L-2)
                if (prev_prefix_idx < 0) { 
                    // If L=1 or L=2, the prefix 1..(L-2) is effectively empty.
                    // An empty prefix can only satisfy the requirement of using 0 doors.
                    if (prev_doors_count == 0) {
                        prev_cost = 0; // Cost is 0 if 0 doors needed for empty prefix.
                    } else {
                        // It's impossible to use a positive number of doors in an empty prefix.
                        prev_cost = INF; 
                    }
                } else { 
                    // If L > 2, the prefix is 1..(L-2). This corresponds to dp table index prev_prefix_idx = L-2.
                    // Check if the required number of doors `prev_doors_count` is non-negative.
                    if (prev_doors_count >= 0) {
                        // Retrieve the precomputed minimum cost for this subproblem.
                        // If the state dp[prev_prefix_idx][prev_doors_count] was impossible to reach,
                        // the DP table would store INF.
                         prev_cost = dp[prev_prefix_idx][prev_doors_count];
                    } else {
                         // It's impossible to need a negative number of doors.
                         prev_cost = INF; 
                    }
                }

                // If the subproblem state dp[L-2][j-k] is reachable (i.e., cost is not INF)
                if (prev_cost != INF) {
                    // Calculate the danger cost of the current component [L, i].
                    // The sum of danger values A_p for p from L to i is S[i] - S[L-1].
                    ll current_comp_sum = S[i] - S[L - 1]; 
                    // The danger cost is the square of the sum.
                    ll current_comp_cost = current_comp_sum * current_comp_sum; 
                    
                    // Update dp[i][j]: take the minimum of the current value (from Option 1 or previous Option 2 iterations)
                    // and the cost obtained by forming component [L, i] (prev_cost + current_comp_cost).
                    dp[i][j] = min(dp[i][j], prev_cost + current_comp_cost);
                }
            } // End loop over k (component length)
        } // End loop over j (number of doors)
    } // End loop over i (door index)

    // After filling the DP table, dp[N][k] contains the minimum cost using exactly k doors among all N doors.
    // Output the results for each k from 1 to N.
    for (int k = 1; k <= N; ++k) {
        cout << dp[N][k] << "\n";
    }

    return 0;
}
0