Codeforces Round #214 (Div. 2) C. Dima and Salad(01背包应用)

题意:

       n个水果,每个水果有一个美味值和卡路里值,从这n个水果中选择任意数目水果,使得最终美味值总和最大,且最终满足等式∑Ai=k*∑Bi。

思路:

       网上题解都说这题01背包,但是自己往01背包上想了很久也没想出思路来。可能是练习的比较少,也可能是背包问题/dp问题的本质没把握好(思想没建立起来),只是死板的往已知模型上靠拢。

       每个物品可以选也可以不选,显然类似于个01背包问题。所以要确定背包容量是啥、物品是啥,物品的价值是啥、物品的体积是啥。(由本题看出,硬往01背包模板上靠其实是很难解决问题的,dp问题好像都有这种特性)。

      首先,将原式移项,可以得到∑Ai-k*∑Bi==0,至于为什么移项处理,我觉的可以作为一种处理问题的基本思想吧。

      其次,拆项得到(A1-k*B1)+(A2-K*B2)+...+(Am-k*Bm)==0,就是说我们从n个物品中最终选出了m件物品使得权值最大且上式==0。(括号中的项一定都是同时存在的)。

      因为最终结果保证==0,所以这m项中既有正数又有负数。所以借助01背包的状态描述,我们可以求出 当前多项式的和 状态下能够得到的最大权值。因为多项式中有负值,我们可以将所有的负数项单独拿出来 做一个背包。所以就是正数做一个背包,负数一个。自然这两个背包中不会有相同的物品且其并集为全部的n个物品。

      相同和值状态下,两个背包中物品相加肯定为零。所以我们从所有状态中选择最大值即可。背包的上限可以去极限情况下的和值100*10==1000(两个背包均是)。

AC代码:

 

#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include<algorithm>
#include <set>
#include <queue>
#include <stack>
#include<vector>
#include<map>
#include<ctime>
#define LL long long
using namespace std;
typedef pair<int,int> ty;
int a[120];
int b[120];
ty c1[120];
ty c2[120];
int dp1[12000];
int dp2[12000];
int main()
{
    int n,k;
    while(cin>>n>>k)
    {
        for(int i=1;i<=n;++i)cin>>a[i];
        for(int i=1;i<=n;++i)cin>>b[i];
        int tot1=0;
        int tot2=0;
        for(int i=1;i<=n;++i)//first是负重,second是价值
        {
            if(a[i]-k*b[i]>=0)c1[++tot1].first=a[i]-k*b[i],c1[tot1].second=a[i];
            else c2[++tot2].first=-a[i]+k*b[i],c2[tot2].second=a[i];
        }
        memset(dp1,-0x3f,sizeof(dp1));
        memset(dp2,-0x3f,sizeof(dp2));
        dp1[0]=0;
        dp2[0]=0;
        for(int i=1;i<=tot1;++i)
            for(int j=11000;j>=c1[i].first;--j)//这个11000是假设全部n个数全部为正数,然后再稍大一点
            {
                dp1[j]=max(dp1[j],dp1[j-c1[i].first]+c1[i].second);
            }
        for(int i=1;i<=tot2;++i)
            for(int j=11000;j>=c2[i].first;--j)
                dp2[j]=max(dp2[j],dp2[j-c2[i].first]+c2[i].second);
        int ans=-1;
        for(int i=0;i<=11000;++i)
            ans=max(ans,dp1[i]+dp2[i]);
        if(!ans)ans=-1;
        cout<<ans<<endl;


    }
    return 0;
}

 

 

      The end;

 

 

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值