负值01背包

题意:n头牛,每头牛有一个幽默值和聪明值(有负数),问如何从中选择k头牛,使得幽默值和聪明值的和非负且最大。

思路:把问题看成01背包,背包的总容量为幽默值的和,每一头牛的幽默值和聪明值分别看成是物品的价值和体积。因为幽默值有可能为负数,不能做数组的下标,所以要先移位。

#include<cstdio>
#include<iostream>
#include<algorithm> 
#include<cstring>
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=2e5+50;
const int shift=1000;
int n,f[maxn],s[maxn],dp[maxn],c,cnt[maxn],ans;
int main(){
	while(scanf("%d",&n)!=EOF){
		memset(f,0,sizeof(f));
		memset(s,0,sizeof(s));
		memset(dp,-inf,sizeof(dp));
		memset(cnt,0,sizeof(cnt));
		dp[0]=0;///除了 背包空间为0的时候最大价值为0外其他背包设置为理论上最小:-inf 
		c=0;
		for(int i=0;i<n;i++){
			scanf("%d%d",&f[i],&s[i]);
			if(s[i]<0&&f[i]<0){///两者都为负数,不取比取好 
				i--,n--;
				continue;
			}
			s[i]+=shift;///移动避免负值 
			c+=s[i];///求背包总容量 
		}
		ans=-inf;
		for(int i=0;i<n;i++)
		for(int j=c;j>=s[i];j--){
			if(dp[j]-cnt[j]*shift<=dp[j-s[i]]+f[i]-(cnt[j-s[i]]+1)*shift){///这里进行了移动预处理,所以要移回去 
				dp[j]=dp[j-s[i]]+f[i];
				cnt[j]=cnt[j-s[i]]+1;
			}
		}
		for(int i=0;i<=c;i++){
			if(i-shift*cnt[i]>=0&&dp[i]>=0){
				ans=max(ans,i-shift*cnt[i]+dp[i]);
			}
		}
		printf("%d\n",ans);
	}
	return 0;
} 

阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页