E. Knapsack
题意
给你很多个物品,每个物品的重量为1,2,3,4,5,6,7,8,现在给出每个重量的物品的种数,问最后容量为W的背包最多装下多重的物品。
1≤w≤1018
1≤cnti≤1016
做法
由于W很大,我们首先可以选出一些肯定会放在最终背包里面的物品,我们假设把背包分成很多个容量为840的背包,我们可以知道的是,每种物品都可以单独装满这个背包,因为840是1−8的最小公倍数,之后我们只要把所有能凑成的840的物品丢尽最终的答案,最后每种物品剩余的个数肯定是<840的,因为第一种最多剩839个,以此类推,最后这些物品的总重量是小于840×8的,我们只需要用一个容量为840×8的背包对剩下的这些物品进行背包就可以了。
代码
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5+5;
ll lef[9];
ll pre[maxn];
int dp[9][maxn];
int main()
{
ll W;
scanf("%lld",&W);
ll sum=0;
for(int i=1;i<=8;i++)
{
scanf("%lld",&lef[i]);
sum=sum+lef[i]*i;
}
if(sum<=W)
{
printf("%lld\n",sum);
return 0;
}
for(int i=1;i<=8;i++)
{
pre[i]=min((ll)840/i,lef[i]);
lef[i]-=pre[i];
}
ll ans=0;
ll tmp=max(0LL,W-840);
for(int i=1;i<=8;i++)
{
ll tt=min(lef[i],(ll)(tmp-ans)/i);
ans=ans+tt*i;
}
memset(dp,0,sizeof(dp));
dp[0][0]=1;
for(int i=1;i<=8;i++)
{
for(int j=0;j<=840*8;j++)
{
for(int k=0;k<=pre[i]&&j-k*i>=0;k++)
dp[i][j]|=dp[i-1][j-k*i];
}
}
ll res=0;
for(int j=0;j<=W-ans;j++) if(dp[8][j]) res=ans+j;
printf("%lld\n",res);
return 0;
}