Poj 2184 Cow Exhibition (DP_背包)

题目链接:http://poj.org/problem?id=2184


题目大意:给定n头牛,每头有属性智商和幽默感,这两个属性值有正有负,现在要从这n头牛中选出若干头使得他们的智商和与幽默感和不为负数,并且两者两家和最大,如果无解输出0,n<=100,-1000<val<1000.


解题思路:变形的01背包,其实问题的本质是保证智商和幽默感和不为负数情况下的最大和。如果一个为负数那肯定不符合情况,我们设智商属性为费用,幽默感属性为价值,问题转换为求费用大等于0时的费用、价值总和。为什么能够转换呢?因为当智商和为x的时候,可能有若干个幽默感和与之对应,但我们只选择一个最大值,所以动态维护这个x对应的最大幽默感和就行了,和上面说的背包模型等价。

    由于本题的费用可能为负数,虽然最后的结果中不能有费用和为负数的情况,但中间的状态可能为负数,而负数的转移过程和正数的方向相反,要分开写。本题我采用Hash的办法,把负数映射到大于20万的地方,其实10万就可以了,但是在允许的情况开大点保险。

    状态转移方程:dp[j] = max(dp[j],dp[j-cost[i]+val[i]) (1<=i<=n,sumpos<=j<=sumneg),复杂度O(N*V)


测试数据:

1
0 1

2
-1 2
2 -2

3
1 2
2 3
3 4

3
-1 -2
-2 -3
-4 -5

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


代码:

#include <stdio.h>
#include <string.h>
#define MIN 110
#define MAX 210000
#define INF 1000000000
#define max(a,b) (a)>(b)?(a):(b)


int ans,cost[MIN],val[MIN];
int n,sumpos,sumneg,dp[MAX*3];


int GetHash(int x) {

	if (x < 0) return MAX - x;
	else return x;
}


int main()
{
	int i,j,k,tpj,tpk;


	while (scanf("%d",&n) != EOF) {

		sumpos = sumneg = 0;
		for (i = 1; i <= n; ++i) {

			scanf("%d%d",&cost[i],&val[i]);
			if (cost[i] > 0) sumpos += cost[i];
			else sumneg += cost[i];
		}


		for (i = sumneg; i <= sumpos; ++i)
			dp[GetHash(i)] = -INF;
		ans = -INF,dp[0] = 0;


		for (i = 1; i <= n; ++i) {

			if (cost[i] >= 0) {

				for (j = sumpos; j >= sumneg + cost[i]; --j){
	
						tpj = GetHash(j);
						tpk = GetHash(j-cost[i]);
						if (dp[tpk] != -INF)
							dp[tpj] = max(dp[tpj],dp[tpk]+val[i]);
					}
			}
			else {
				
				for (j = sumneg; j <= sumpos + cost[i]; ++j) {
						
						tpj = GetHash(j);
						tpk = GetHash(j-cost[i]);
						if (dp[tpk] != -INF)
							dp[tpj] = max(dp[tpj],dp[tpk]+val[i]);
					}
			}
		}


		for (i = 0; i <= sumpos; ++i)
			if (dp[i] >= 0) ans = max(ans,dp[i]+i);
		printf("%d\n",ans);
	}
}

本文ZeroClock原创,但可以转载,因为我们是兄弟。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值