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


E. Knapsack

题意

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

1w10181 \leq w \leq 10^{18}1≤w≤1018
1cnti10161 \leq cnt_i \leq 10^{16}1≤cnti​≤1016
做法

由于WWW很大,我们首先可以选出一些肯定会放在最终背包里面的物品,我们假设把背包分成很多个容量为840840840的背包,我们可以知道的是,每种物品都可以单独装满这个背包,因为840840840是181-81−8的最小公倍数,之后我们只要把所有能凑成的840840840的物品丢尽最终的答案,最后每种物品剩余的个数肯定是&lt;840&lt;840<840的,因为第一种最多剩839839839个,以此类推,最后这些物品的总重量是小于840×8840\times8840×8的,我们只需要用一个容量为840×8840\times8840×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;
}

上一篇:Leetcode_med 81. 搜索旋转排序数组 II


下一篇:(81)zabbix监控mysql