POJ 2148 Cow Exhibition——01背包方法遇到负状态的处理方法

Description

“Fat and docile, big and dumb, they look so stupid, they aren’t much
fun…”

  • Cows with Guns by Dana Lyons

The cows want to prove to the public that they are both smart and fun. In order to do this, Bessie has organized an exhibition that will be put on by the cows. She has given each of the N (1 <= N <= 100) cows a thorough interview and determined two values for each cow: the smartness Si (-1000 <= Si <= 1000) of the cow and the funness Fi (-1000 <= Fi <= 1000) of the cow.

Bessie must choose which cows she wants to bring to her exhibition. She believes that the total smartness TS of the group is the sum of the Si’s and, likewise, the total funness TF of the group is the sum of the Fi’s. Bessie wants to maximize the sum of TS and TF, but she also wants both of these values to be non-negative (since she must also show that the cows are well-rounded; a negative TS or TF would ruin this). Help Bessie maximize the sum of TS and TF without letting either of these values become negative.

Input

  • Line 1: A single integer N, the number of cows

  • Lines 2…N+1: Two space-separated integers Si and Fi, respectively the smartness and funness for each cow.

Output

  • Line 1: One integer: the optimal sum of TS and TF such that both TS and TF are non-negative. If no subset of the cows has non-negative TS and non- negative TF, print 0.

Sample Input

5
-5 7
8 -6
6 -3
2 1
-8 -5

Sample Output

8

Hint

OUTPUT DETAILS:

Bessie chooses cows 1, 3, and 4, giving values of TS = -5+6+2 = 3 and TF
= 7-3+1 = 5, so 3+5 = 8. Note that adding cow 2 would improve the value
of TS+TF to 10, but the new value of TF would be negative, so it is not
allowed.

题目大意:

每行给出si和fi,代表牛的两个属性,然后要求选出几头牛,求出总S与总F的和的最大值,并且总s和总f都不能是负数。

题目分析

方法一:dfs(超时方法)

  1. 采用深搜的方法来解决,因为每头牛要么选要么不选,所以很容易想到深搜,思路清晰简单。
  2. 但是对于这道题来说,n最多可达100,可选可不选,也就是2的100次,这中数量级无疑肯定超时,所以咱也就说说怎么个思路就行。
#include<iostream>
#include<cstdio>
#include<string>
#include<limits.h>
#include<algorithm>
#include<cstring>
#include<math.h>
#include<set>
#include<vector>
#include<queue>
using namespace std;
typedef long long ll;
#define inf	INT_MAX
struct Cow{
	int s;
	int f;
	Cow(){}
	Cow(int x,int y):s(x),f(y){}
	/*bool operator< (const Cow& other)const{
		return s+f>(other.s+other.f);
	}*/
}cow[101];
int sum=0;
int res=0;
void dfs(int ts,int tf,int n){
	
	if(ts+tf>res&&ts>=0&&tf>=0){
		res=tf+ts;
	}
	if(ts+tf<0){
		return;
	}
	if(n==sum){
		return;
	}
	dfs(ts+cow[n].s,tf+cow[n].f,n+1);
	dfs(ts,tf,n+1);
}
int main(){
	int n,x,y;
	int ts=0,tf=0;
	while(scanf("%d",&n)!=EOF){
		ts=tf=0;
		sum=0;
		res=0;
		for(int i=0;i<n;i++){
			scanf("%d%d",&x,&y);
			if(x<=0&&y<=0){
				continue;
			}else{
				cow[sum].s=x;
				cow[sum++].f=y;
			}
		}
		dfs(ts,tf,0);
		
		printf("%d\n",res);
	}
}

方法二(动态规划AC):

  1. 因为每头牛可选可不选,这不正好是01背包问题么,但是又不同于典型的01背包问题,我们需要重新定义这个dp数组以及它的含义。
  2. 这里dp【i】中,i代表s属性值,dp【i】本身代表的是s为i时f的最大值,所以整个的i+dp【i】就是咱最后要求的总s和总f。
  3. 但是这道题 里面状态有正有负,范围是-1000100-1000100,数组索引不能是负的把,所以咱就必须要平移原点,使得最小的-1000100为0,那么原点就是1000100这个位置了。
  4. 那接下来咱就接着说状态是怎么个转移法,前面也说了,就跟背包问题差不多。但区别在于这里状态可能会出现负数。
    单纯的背包问题,一维数组都是逆序,为的就是能在上一次求出结果的基础上更新值,这里当s值为非负值时,没问题,逆序可以满足条件。
    但是当s为负值呢,你再逆序会出现什么情况,dp【i-s】,这里i-s比i还大,意思就拿更新之后的值去更新dp【i】,可不就错了么。
    所以,负数需要正序考虑。
    不管正序逆序,咱都是要用上一次的值更新,要符合01背包的定义
#include<iostream>
#include<cstdio>
#include<string>
#include<limits.h>
#include<algorithm>
#include<cstring>
#include<math.h>
#include<set>
#include<vector>
#include<queue>
using namespace std;
typedef long long ll;
#define inf	-1000000
struct Cow {
	int s;
	int f;
	Cow() {}
	Cow(int x,int y):s(x),f(y) {}
	/*bool operator< (const Cow& other)const{
		return s+f>(other.s+other.f);
	}*/
} cow[101];
int sum=0;
int dp[1000*100*2+1];
int main() {
	int n,x,y;
	
	while(scanf("%d",&n)!=EOF) {
		int res=0;
		sum=0;
		fill_n(dp,1000*100*2+1,inf);
		dp[1000*100]=0;
		for(int i=0; i<n; i++) {
			scanf("%d%d",&x,&y);
			if(x<=0&&y<=0) {
				continue;
			} else {
				cow[sum].s=x;
				cow[sum++].f=y;
			}
		}
		for(int i=0; i<sum; i++) {
			if(cow[i].s>=0) {
				for(int j=1000*100*2; j>=cow[i].s; j--) {
					if(dp[j-cow[i].s]>inf) {
						dp[j]=max(dp[j],dp[j-cow[i].s]+cow[i].f);
					}
				}
			} else {
				for(int j=0; j<=1000*100*2+cow[i].s; j++) {
					if(dp[j-cow[i].s]>inf) {
						dp[j]=max(dp[j],dp[j-cow[i].s]+cow[i].f);
					}
				}
			}
		}
		for(int i=1000*100; i<=1000*100*2; i++) {
			if(dp[i]>=0) {
				res=max(res,i-1000*100+dp[i]);
			}
		}
		printf("%d\n",res);
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值