2400专项:E. Knapsack(思维dp 大背包)

原题: http://codeforces.com/contest/1132/problem/E

题意: a 1 a_1 个1, a 2 a_2 个2…… a 8 a_8 个8,问大小为 W W 背包可以装下的最大 s i z siz a i < 1 e 16 a_i<1e16 W < 1 e 18 W<1e18

解析:

虽然每个数的数量很大,但是局限于只有8种数,所以组合的重复率会很高。

怎么说?1到8的LCM为840,当2的数量大于等于420时,我们只需要记录下1,8的数量大于等于120时,也记录下1。这个1可以在不同的方案中起到相同的作用。

再想一下,420个2可以记录下来,但是419个2和1个3能不能记录下1,再转移到状态1呢?显然不行。之前记录下1是因为状态 X X 可以通过加减 420 420 2 2 来转移到相邻的状态 X X ,而419个2和1个3就不行。

如果用 d p [ i ] [ j ] dp[i][j] 表示到了第 i i 个数,状态为 j j 时的最大840个数。那么通过上面的结果可以得出:(共 v v i i ,选出 k k i i d p [ i 1 ] [ j ] + ( v k ) / ( 840 / i ) ) d p [ i ] [ j + k i ] dp[i-1][j]+(v-k)/(840/i))\to dp[i][j+k*i]

#include<bits/stdc++.h>
using namespace std;
#define LL long long
LL dp[10][10000];
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    LL w;cin>>w;
    LL v;
    memset(dp,0xc0,sizeof(dp));
    dp[0][0]=0;
    LL ma=8*840;
    for(LL i=1;i<=8;i++){
        cin>>v;
        for(LL j=0;j<ma;j++){
            for(LL k=min((840/i)-1ll,v);k>=0;k--){
                if(dp[i-1][j]>=0)
                    dp[i][j+k*i]=max(dp[i][j+k*i],dp[i-1][j]+(v-k)/(840/i));
            }
        }
    }

    LL ans=-1;
    for(LL i=0;i<min(ma,w+1);i++){
        if(dp[8][i]>=0)
            ans=max(ans,i+840*min(dp[8][i],w>=i?(w-i)/840:0));
    }
    cout<<ans<<endl;
}

猜你喜欢

转载自blog.csdn.net/jk_chen_acmer/article/details/89319744