結果

問題 No.1947 質より種類数
ユーザー qwewe
提出日時 2025-05-14 13:13:22
言語 C++17
(gcc 13.3.0 + boost 1.87.0)
結果
AC  
実行時間 1,147 ms / 2,000 ms
コード長 6,648 bytes
コンパイル時間 875 ms
コンパイル使用メモリ 83,580 KB
実行使用メモリ 6,272 KB
最終ジャッジ日時 2025-05-14 13:14:18
合計ジャッジ時間 6,041 ms
ジャッジサーバーID
(参考情報)
judge1 / judge4
このコードへのチャレンジ
(要ログイン)
ファイルパターン 結果
other AC * 37
権限があれば一括ダウンロードができます

ソースコード

diff #

#include <iostream>
#include <vector>
#include <deque>
#include <algorithm>

// Using namespace std to simplify code writing (e.g., std::vector -> vector)
using namespace std;

// Define long long type alias for potentially large satisfaction values
// This helps avoid integer overflow issues.
typedef long long ll;

// State structure to store elements in the monotonic queue (deque).
// Each element corresponds to a state processed at a particular multiple 'q'.
// It stores 'q' and the calculated value 'val' which is used for optimization.
// Specifically, val = dp[q * v_i + r] - q * w_i
struct State {
    int q;    // The multiple index q
    ll val;   // The calculated value V(q) = dp[q * v_i + r] - q * w_i
};

int main() {
    // Faster I/O operations by decoupling C++ standard streams from C stdio
    ios_base::sync_with_stdio(false);
    // Untie cin from cout to potentially speed up input operations
    cin.tie(NULL);

    int N; // Number of item types
    ll V; // Total budget
    ll C; // Coefficient for the reward based on the number of distinct item types purchased
    cin >> N >> V >> C;

    // Vectors to store item properties: v[i] is the price, w[i] is the value for item type i
    vector<ll> v(N), w(N);
    for (int i = 0; i < N; ++i) {
        cin >> v[i] >> w[i];
    }

    // DP table: dp[j] stores the maximum satisfaction achievable with a budget of exactly j
    // Initialize all entries to 0. dp[0] = 0 means 0 satisfaction with 0 budget (buy nothing).
    // Other dp[j] = 0 initially represents the minimum possible satisfaction (non-negative).
    vector<ll> dp(V + 1, 0); 

    // Iterate through each item type to build the DP solution
    for (int i = 0; i < N; ++i) {
        // According to constraints, v[i] >= 1, so price is positive.
        // If the problem allowed v[i] <= 0, we might need a check here.
        // Example: if (v[i] <= 0) continue; 

        // Create a temporary DP table (dp_new) initialized with the current state of dp.
        // This is crucial because the calculations for item 'i' must be based on the results
        // from items 0 to i-1. Using dp_new prevents updates within the current item's loops
        // from affecting subsequent calculations for the same item, which would violate the
        // logic of the multiple choice knapsack problem formulation.
        vector<ll> dp_new = dp; 

        // Apply the monotonic queue optimization technique.
        // This requires processing budgets based on their remainder modulo v[i].
        // Iterate through all possible remainders r from 0 to v[i]-1.
        for (int r = 0; r < v[i]; ++r) {
            // Initialize an empty deque (double-ended queue) for this remainder r.
            // The deque will store State objects, maintaining the monotonic property needed for optimization.
            deque<State> Q; 

            // Iterate through multiples q = 0, 1, 2, ...
            // such that the current budget level current_j = q * v[i] + r does not exceed V.
            for (int q = 0; ; ++q) {
                ll current_j = (ll)q * v[i] + r; // Calculate current budget level
                if (current_j > V) {
                    break; // Stop if the budget exceeds the limit V
                }

                // If the queue is not empty, we can potentially update dp_new[current_j].
                // The front element of the queue Q.front() corresponds to the state 'x'
                // that maximizes V(x) = dp[x*v_i+r] - x*w[i] among the relevant previous states x < q.
                if (!Q.empty()) {
                    ll max_prev_V = Q.front().val; // Get the maximum V(x) value from the front
                    
                    // Calculate the potential satisfaction achieved by choosing item type i.
                    // The formula derived from the optimization is max_prev_V + C + q * w[i].
                    // This represents the maximum satisfaction achievable by transitioning from a previous state 'x'
                    // (captured by max_prev_V) and purchasing item type i (at least once, total k = q-x items).
                    // The total satisfaction contribution is C + k*w[i].
                    // Adding this to the base satisfaction dp[x*v_i+r] and simplifying algebraically gives this formula.
                    ll potential_satisfaction = max_prev_V + C + (ll)q * w[i];
                    
                    // Update the satisfaction for budget current_j if this path yields a higher value.
                    // It compares against the value already in dp_new[current_j], which could be:
                    // 1. The value from the previous DP state `dp[current_j]` (i.e., not using item `i`).
                    // 2. A value updated by processing a previous remainder `r'` for the same item `i`.
                    dp_new[current_j] = max(dp_new[current_j], potential_satisfaction);
                }
                 
                // Calculate V(q) = dp[current_j] - q * w[i].
                // This calculation must use the DP state *before* considering item i.
                // `dp[current_j]` holds the values from the previous iteration (items 0 to i-1).
                ll current_Vq = dp[current_j] - (ll)q * w[i];

                // Maintain the monotonic property of the queue: values `val` should be strictly decreasing.
                // Remove elements from the back of the queue whose `val` are less than or equal to `current_Vq`.
                // This ensures that the queue only stores potentially optimal states.
                while (!Q.empty() && Q.back().val <= current_Vq) {
                    Q.pop_back();
                }
                // Add the current state represented by {q, current_Vq} to the back of the queue.
                Q.push_back({q, current_Vq});
            }
        }
        // After processing all remainders for the current item i, update the main DP table `dp`
        // with the newly computed values from `dp_new`. This prepares `dp` for the next iteration (next item).
        dp = dp_new;
    }

    // The final answer is the maximum satisfaction value found in the DP table across all possible
    // budget levels from 0 to V. Since satisfaction might not increase monotonically with budget
    // (e.g., spending more might force suboptimal choices), we must check all budgets.
    ll max_satisfaction = 0;
    for (ll j = 0; j <= V; ++j) {
        max_satisfaction = max(max_satisfaction, dp[j]);
    }

    // Output the final maximum satisfaction value.
    cout << max_satisfaction << endl;

    return 0; // Indicate successful execution
}
0