Submission #1347423

#TimeUsernameProblemLanguageResultExecution timeMemory
1347423sugargodKnapsack (NOI18_knapsack)C++20
100 / 100
19 ms1888 KiB
#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

// Use long long for values to avoid overflow
typedef long long ll;

// A simple structure to represent an item in our final DP list
struct FinalItem {
    ll value;
    ll weight;
};

// This is the comparison function. 
// It tells the sort algorithm: "Put the item with the higher value first."
bool compareByValue(const pair<int, int>& a, const pair<int, int>& b) {
    return a.first > b.first; 
}

int main() {
    // Speeds up input and output
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);

    int S, N;
    if (!(cin >> S >> N)) return 0;

    // weight_groups[w] stores a list of {value, quantity} for weight 'w'
    vector<vector<pair<int, int>>> weight_groups(S + 1);

    for (int i = 0; i < N; i++) {
        int v, w, k;
        cin >> v >> w >> k;
        if (w <= S) {
            weight_groups[w].push_back({v, k});
        }
    }

    vector<FinalItem> items_to_process;

    // Process each weight from 1 to S
    for (int w = 1; w <= S; w++) {
        if (weight_groups[w].empty()) continue;

        // Use our custom function to sort items of this weight by value
        sort(weight_groups[w].begin(), weight_groups[w].end(), compareByValue);

        // We only need enough items to fill the total capacity S.
        // If weight is w, we can fit at most S/w items.
        int total_needed = S / w;
        int collected_so_far = 0;

        for (int i = 0; i < weight_groups[w].size(); i++) {
            int val = weight_groups[w][i].first;
            int qty = weight_groups[w][i].second;

            // How many can we take from this specific item type?
            int can_take = min(qty, total_needed - collected_so_far);
            
            if (can_take <= 0) break; // We have enough items of this weight
            collected_so_far += can_take;

            // Binary Splitting: instead of adding 'can_take' items one by one,
            // we group them into powers of 2 (1, 2, 4, 8...).
            // This is a standard trick to solve Bounded Knapsack efficiently.
            for (int p = 1; can_take > 0; p <<= 1) {
                int batch_size = min(p, can_take);
                items_to_process.push_back({(ll)batch_size * val, (ll)batch_size * w});
                can_take -= batch_size;
            }
        }
    }

    // Standard 0/1 Knapsack DP
    // dp[j] = maximum value we can get with total weight j
    vector<ll> dp(S + 1, 0);

    for (int i = 0; i < items_to_process.size(); i++) {
        ll item_v = items_to_process[i].value;
        ll item_w = items_to_process[i].weight;

        // Standard Knapsack loop: iterate backwards to ensure each 
        // split item is used only once.
        for (int j = S; j >= item_w; j--) {
            if (dp[j - item_w] + item_v > dp[j]) {
                dp[j] = dp[j - item_w] + item_v;
            }
        }
    }

    cout << dp[S] << endl;

    return 0;
}
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...