原题:
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,每个物品的数量对应为
c
n
t
1
cnt_1
cnt1到
c
n
t
8
cnt_8
cnt8,给你一个容量为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分成两个部分 W 1 W_1 W1和 W 2 W_2 W2,其中较大的部分 W 1 W_1 W1使用贪心的方法解决。剩余的部分 W 2 W_2 W2使用多重背包的方式解决。
由于物品的种类和价值只有固定的8种,对应1到8,物品价值和种类相同,且只有8种,可以求得1到8的最小公倍数为840,840可以任意整除1到8种的任意数字。
以840作为 W 2 W_2 W2的初始值,初始值的意思是这样,因为对 W 1 W_1 W1进行贪心求解,不一定正好得到的使得贪心的结果使得正好装满 W 1 W_1 W1。
首先将每种物品的数量 c n t i cnt_i cnti分成两份,分别为 p i p_i pi和 q i q_i qi,有关系如下
p i = m i n ( c n t [ i ] , 840 / i ) p_i=min(cnt[i],840/i) pi=min(cnt[i],840/i) 用作可以放在 W 2 W_2 W2种使用多重背包求解的每个物体数量
q i = c n t i − p i q_i=cnt_i-p_i qi=cnti−pi 由于 q i q_i qi数量较大(当 c n t i cnt_i cnti很大时),将这部分物体直接用贪心的方法解决。
使用贪心的方式计算得到的最大价值不一定正好填满 W 1 W_1 W1,记录此时装入物体的临时结果 r e s res res,并此时更新 W 1 W_1 W1与 W 2 W_2 W2
由于新的 W 2 W_2 W2很有可能大于840,极端情况下就是剩余了8*840容量,此时可用多重背包对剩余的容量进行计算,得到最优值,与前面得到的临时结果 r e s res res相加即可
这题曾经在UVa上做过类似的,当年秒出的题,时间长了居然看了半天题解才明白=_=