一道很神奇的背包题。
题目链接:http://codeforces.com/contest/1132/problem/E
题意:
给你一个背包上限w和数字1-8的个数,注意w<=1e18,num[i]<=1e16。
似乎是一个巨大的背包,让我真的是,有点摸不到头脑啊。。想了一段时间也确实没想到,知道肯定是个背包就对了。。
做法:
看了网上大佬的做法,很神奇,dp[i][j]表示for到数字i时能装到j的840的最大个数,因为840为1-8的最小公倍数,所以等于把所有的数字都处理到840以内,比如840/2=420,所以最后2剩下来用来背包的部分最多为419个,其他的都直接喂840了,然后就把这些数字用来背包,背包的上限就是840*8,也就是极限大家都差一点点的情况。
具体的东西写在代码里。
#include<bits/stdc++.h>
using namespace std;
const int maxn=10005;
const int up=8*840;
typedef long long ll;
ll dp[10][maxn],a[10],w;
int main(){
scanf("%lld",&w);
for(int i=1;i<=8;i++) scanf("%lld",&a[i]);
memset(dp,-1,sizeof(dp));
dp[0][0]=0;
for(int i=1;i<=8;i++){
for(int j=0;j<=up;j++){
if(dp[i-1][j]==-1) continue;
ll most=min((ll)840/i,a[i]);
//在需要最多i和i现在有多少里取一个上限
for(int k=0;k<=most;k++){
dp[i][j+k*i]=max(dp[i][j+k*i],dp[i-1][j]+(a[i]-k)/(840/i));
//我不用来喂840的部分是给j的,其他的都用来喂840
}
}
}
ll ans=0;
for(int i=0;i<=up;i++){
if(i>w||dp[8][i]==-1) continue;
ans=max(ans,i+840*min(dp[8][i],(w-i)/840));
//在我能取到的最大数量和不会超过w里取一个最大值
}
printf("%lld\n",ans);
return 0;
}