题意:
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;