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上做过类似的,当年秒出的题,时间长了居然看了半天题解才明白=_=

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