Educational Codeforces Round 61 (Rated for Div. 2) E. Knapsack 背包

一道很神奇的背包题。

题目链接: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
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;
}

 

你可能感兴趣的:(dp,思维)