有几个数,每个数两个权值,取其中的一些数,使得k个数,2k个权值和最大(权值有正有负),限制:两种权值分别的权值和都得为非负。
这个有点意思!
首先负状态的推移我就不说了,可以看看这篇http://blog.csdn.net/vmurder/article/details/39472659
然后是下一个知识点,就是说正常的01背包是二维的状态f[i][j]表示第i个物品时占用j个体积时能得到的最大价值或者什么乱七八糟的,因为可以有序地进行转移,所以可以把状态压成一维的(通过逆序循环使调用状态为上一个物品留下的状态),但如果是有体积为负的呢?
好吧,正着循环!判断一下体积正负就好了……
贴代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 105
#define M 101000
using namespace std;
int n,suma,sumA,sumb,sumB;
int f[M<<1],a[N],b[N];
int main()
{
// freopen("test.in","r",stdin);
int i,j,k;
scanf("%d",&n);
for(i=1;i<=n;i++)
{
scanf("%d%d",&a[i],&b[i]);
if(a[i]<0)suma+=a[i];
else sumA+=a[i];
if(b[i]<0)sumb+=b[i];
else sumB+=b[i];
}
memset(f,0xc0,sizeof(f));
f[M]=0;
for(i=1;i<=n;i++)
{
if(a[i]<=0)for(j=M+suma;j<=M+sumA;j++)f[j]=max(f[j],f[j-a[i]]+b[i]);
else for(j=M+sumA;j>=M+suma;j--)f[j]=max(f[j],f[j-a[i]]+b[i]);
}
int ans=0;
for(i=M;i<=M+sumA;i++)if(f[i]>=0)ans=max(ans,f[i]+i-M);
printf("%d\n",ans);
return 0;
}