cf Educational Codeforces Round 61 E. Knapsack

原題:
E. Knapsack
time limit per test2 seconds
memory limit per test256 megabytes
inputstandard input
outputstandard output
You have a set of items, each having some integer weight not greater than 8. You denote that a subset of items is good if total weight of items in the subset does not exceed W.

You want to calculate the maximum possible weight of a good subset of items. Note that you have to consider the empty set and the original set when calculating the answer.

Input
The first line contains one integer W (0≤W≤10^18) — the maximum total weight of a good subset.

The second line denotes the set of items you have. It contains 8 integers cnt1, cnt2, …, cnt8 (0≤cnti≤10^16), where cnti is the number of items having weight i in the set.

Output
Print one integer — the maximum possible weight of a good subset of items.

Examples
input
10
1 2 3 4 5 6 7 8
output
10
input
0
0 0 0 0 0 0 0 0
output
0
input
3
0 4 1 0 0 9 8 3
output
3

中文:
給你8個物品,這8個物品的價值是分別是1到8,每個物品的數量對應爲cnt1cnt_1cnt8cnt_8,給你一個容量爲W的揹包,現在問你最多能得到多少價值?(W超大)

代碼:

#include<bits/stdc++.h>
using namespace std;
 
typedef long long ll;
const ll N = 840;
 
ll W,a[9],cnt[9];
int dp[9][N*8+1];
int main()
{
    ios::sync_with_stdio(false);
    while(cin>>W)
    {
        ll ans=0,tot=0,tmp=0,res=0;
        memset(dp,0,sizeof(dp));
        for(ll i=1;i<=8;i++)
        {
            cin>>a[i];
            tot+=a[i]*i;
            cnt[i]=min(a[i],N/i);
            a[i]-=cnt[i];
        }
        if(tot<=W)
        {
            cout<<tot<<endl;
            continue;
        }
        if(W-N>0)
            tmp=W-N;
        ll val;
        for(ll i=1;i<=8;i++)
        {
            val=min(a[i],(tmp-res)/i);
            res+=val*i;
        }
        dp[0][0]=1;
        for(int i=1;i<=8;i++)
        {
            for(int j=0;j<=N*8;j++)
            {
                for(int k=0;k<=cnt[i];k++)
                {
                    if(j-i*k>=0)
                        dp[i][j]|=dp[i-1][j-i*k];
                }
            }
        }
        for(ll i=W-res;i>=0;i--)
        {
            if(dp[8][i])
            {
                ans=res+i;
                break;
            }
        }
        cout<<ans<<endl;
 
    }
    return 0;
}

思路:

參考該博客

由於揹包的容量和每樣物品給定的數量巨大,不可能直接用多重揹包或搜索直接求解。那麼可以考慮講W分成兩個部分W1W_1W2W_2,其中較大的部分W1W_1使用貪心的方法解決。剩餘的部分W2W_2使用多重揹包的方式解決。

由於物品的種類和價值只有固定的8種,對應1到8,物品價值和種類相同,且只有8種,可以求得1到8的最小公倍數爲840,840可以任意整除1到8種的任意數字。

以840作爲W2W_2的初始值,初始值的意思是這樣,因爲對W1W_1進行貪心求解,不一定正好得到的使得貪心的結果使得正好裝滿W1W_1

首先將每種物品的數量cnticnt_i分成兩份,分別爲pip_iqiq_i,有關係如下

pi=min(cnt[i],840/i)p_i=min(cnt[i],840/i) 用作可以放在W2W_2種使用多重揹包求解的每個物體數量

qi=cntipiq_i=cnt_i-p_i 由於qiq_i數量較大(當cnticnt_i很大時),將這部分物體直接用貪心的方法解決。

使用貪心的方式計算得到的最大價值不一定正好填滿W1W_1,記錄此時裝入物體的臨時結果resres,並此時更新W1W_1W2W_2

由於新的W2W_2很有可能大於840,極端情況下就是剩餘了8*840容量,此時可用多重揹包對剩餘的容量進行計算,得到最優值,與前面得到的臨時結果resres相加即可

這題曾經在UVa上做過類似的,當年秒出的題,時間長了居然看了半天題解才明白=_=

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章