結果
問題 |
No.2026 Yet Another Knapsack Problem
|
ユーザー |
![]() |
提出日時 | 2025-05-14 13:10:54 |
言語 | C++17 (gcc 13.3.0 + boost 1.87.0) |
結果 |
TLE
|
実行時間 | - |
コード長 | 7,425 bytes |
コンパイル時間 | 1,042 ms |
コンパイル使用メモリ | 91,684 KB |
実行使用メモリ | 16,072 KB |
最終ジャッジ日時 | 2025-05-14 13:13:32 |
合計ジャッジ時間 | 73,304 ms |
ジャッジサーバーID (参考情報) |
judge3 / judge2 |
(要ログイン)
ファイルパターン | 結果 |
---|---|
sample | AC * 3 |
other | AC * 35 TLE * 1 -- * 6 |
ソースコード
#include <iostream> #include <vector> #include <deque> #include <algorithm> using namespace std; // Define long long for convenience typedef long long ll; // Define a very small negative value to represent infinity (unreachable states). // The maximum possible value sum is approximately N * 10^9 = 2500 * 10^9 = 2.5 * 10^12. // The minimum possible value sum is approximately N * (-10^9) = -2.5 * 10^12. // Choose NINF sufficiently small. -3 * 10^18 should be safe. const ll NINF = -3e18; int main() { // Use fast I/O ios_base::sync_with_stdio(false); cin.tie(NULL); int N; // Number of item types and maximum weight capacity cin >> N; // Store counts and values for each item type vector<int> counts(N + 1); vector<ll> values(N + 1); for (int i = 1; i <= N; ++i) { cin >> counts[i] >> values[i]; } // Initialize DP table. dp[k][w] stores the maximum value achievable // using exactly k items with a total weight of exactly w. vector<vector<ll>> dp(N + 1, vector<ll>(N + 1, NINF)); // Base case: 0 items, 0 weight results in 0 value. dp[0][0] = 0; // Iterate through each item type from 1 to N for (int i = 1; i <= N; ++i) { int c_i = counts[i]; // Number of items of type i available ll v_i = values[i]; // Value of one item of type i int weight = i; // Weight of one item of type i // Basic check: Item weight must be positive (guaranteed by constraints 1 <= i <= N). if (weight == 0) continue; // Create a temporary DP table `next_dp` initialized with the current `dp` state. // This ensures that updates for item type `i` are based on the results // from processing items 1 to `i-1`. Using `std::move` later optimizes assignment. vector<vector<ll>> next_dp = dp; // Apply the monotonic queue optimization for bounded knapsack. // Iterate through all possible remainders `r` when weight `w` is divided by `weight`. for (int r = 0; r < weight; ++r) { // Calculate the maximum possible quotient `q` such that `q * weight + r <= N`. int max_q_for_r = (N - r) / weight; // If `N < r`, then `max_q_for_r` can be negative. No states to process for this `r`. if (max_q_for_r < 0) continue; // Calculate the theoretical range of `D = k - q`. // Minimum D occurs when k=0 and q=max_q_for_r: D = -max_q_for_r. // Maximum D occurs when k=N and q=0: D = N. int min_D = -max_q_for_r; int max_D = N; int D_range_size = max_D - min_D + 1; // If the range size is non-positive, skip. if (D_range_size <= 0) continue; // Calculate offset to map D values to non-negative indices for vector access. int offset = -min_D; // `D_deques` stores one deque for each possible value of D. // Each deque stores pairs `(q, f(q))`, where `f(q) = dp[k][w] - q * v_i`. vector<deque<pair<int, ll>>> D_deques(D_range_size); // Iterate through possible quotients `q` from 0 to `max_q_for_r`. for (int q = 0; q <= max_q_for_r; ++q) { int w = q * weight + r; // Current total weight // Determine the relevant range of D for the current `q`. // Since `k = D + q`, we must have `0 <= k <= N`, which implies `-q <= D <= N - q`. // Intersect this with the theoretical range `[min_D, max_D]`. int current_min_D = max(min_D, -q); int current_max_D = min(max_D, N - q); // Iterate through the relevant D values for the current `q`. for (int D = current_min_D; D <= current_max_D; ++D) { int k = D + q; // Current number of items // Get the specific deque for the current D value using the offset. deque<pair<int, ll>>& dq = D_deques[D + offset]; // Remove elements from the front of the deque whose quotient `q'` is // outside the sliding window `[q - c_i, q]`. The window size corresponds // to using 0 up to `c_i` items of type `i`. while (!dq.empty() && dq.front().first < q - c_i) { dq.pop_front(); } // Update `next_dp[k][w]` using the maximum value from the deque. // The maximum `f(q')` value in the window is at the front of the deque. if (!dq.empty()) { ll max_f_q_prime = dq.front().second; // Check if the value is valid (not derived from an initial NINF state). // Use NINF / 2 as a threshold to avoid potential issues near NINF boundary. if (max_f_q_prime > NINF / 2) { // Calculate the new potential value: `f(q') + q * v_i` ll new_val = max_f_q_prime + (ll)q * v_i; // Update `next_dp[k][w]` if this new value is better. next_dp[k][w] = max(next_dp[k][w], new_val); } } // Consider the value from the DP state *before* processing item `i` (stored in `dp`). // This value `dp[k][w]` represents the max value using items 1 to `i-1`. // It's used to maintain the deque for subsequent `q` values. ll current_dp_val = dp[k][w]; // Check if the state `(k, w)` was reachable before considering item `i`. if (current_dp_val > NINF / 2) { // Calculate `f(q) = dp[k][w] - q * v_i`. ll f_q = current_dp_val - (ll)q * v_i; // Maintain the monotonic property of the deque: `f(q)` values should be non-increasing. // Remove elements from the back of the deque that are dominated by the current `f(q)`. while (!dq.empty() && dq.back().second <= f_q) { dq.pop_back(); } // Add the current state information `(q, f(q))` to the back of the deque. dq.push_back({q, f_q}); } } } } // After processing item type `i` for all remainders `r`, update the main DP table `dp`. // `std::move` is used for efficiency to transfer ownership of the data. dp = std::move(next_dp); } // After processing all item types, find and output the answer for each `k` from 1 to N. for (int k = 1; k <= N; ++k) { ll max_v = NINF; // Initialize max value for k items to NINF. // Find the maximum value among all possible weights `w` (0 to N) for fixed `k`. for (int w = 0; w <= N; ++w) { max_v = max(max_v, dp[k][w]); } // Output the maximum value found for `k` items. // The problem guarantees that a solution exists for each k, so max_v should be updated from NINF. cout << max_v << "\n"; } return 0; }