【Educational Codeforces Round 61 (Rated for Div. 2) E. Knapsack】贪心+背包


E. Knapsack

题意

给你很多个物品,每个物品的重量为 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 1,2,3,4,5,6,7,8 1,2,3,4,5,6,7,8,现在给出每个重量的物品的种数,问最后容量为W的背包最多装下多重的物品。

1 ≤ w ≤ 1 0 18 1 \leq w \leq 10^{18} 1w1018
1 ≤ c n t i ≤ 1 0 16 1 \leq cnt_i \leq 10^{16} 1cnti1016
做法

由于 W W W很大,我们首先可以选出一些肯定会放在最终背包里面的物品,我们假设把背包分成很多个容量为 840 840 840的背包,我们可以知道的是,每种物品都可以单独装满这个背包,因为 840 840 840 1 − 8 1-8 18的最小公倍数,之后我们只要把所有能凑成的 840 840 840的物品丢进最终的答案,最后每种物品剩余的个数肯定是 < 840 <840 <840的,因为第一种最多剩 839 839 839个,以此类推,最后这些物品的总重量是小于 840 × 8 840\times8 840×8的,我们只需要用一个容量为 840 × 8 840\times8 840×8的背包对剩下的这些物品进行背包就可以了。

代码

#include
#include
#include
#include
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;
}

你可能感兴趣的:(Codeforces,DP)