poj 2184 dp(兼具聪明和幽默的奶牛)

题意:有n头奶牛,每个奶牛有一个聪明值(s)和幽默值(f)(-1000<=值<=1000)。现欲选出若干奶牛,使得这些奶牛的聪明值之和与幽默值之和再求和所得的数值最大,且需要满足聪明值之和和幽默值之和都非负。

思路:一开始以为和1015陪审团是同样的思路,后来发现不行。于是参考了http://www.cnblogs.com/rainydays/archive/2012/07/04/2576077.html

思路变为d[i][s]=f的形式。表示用前i头牛构成s属性和为s的情况下f属性和最大为多少。状态转移从两种情况来,即用或者不用当前的牛。dp[i][j] = max(dp[i - 1][j - s[i]] + f[i], dp[i - 1][j])。在实际操作的时候可以将第一维去掉,进行空间上的优化。但是由于s[i]的值有正有负,所以在填写数组的顺序要根据s[i]的值来决定,若为正则从右到左(类似01背包的空间优化),若为负则从左到右。
注意:动态规划中状态维和值是可以相互转化的。状态维过多,效率低的时候,可以把将其转化为数组值;同理,数组值不唯一无法规划时,可以增加状态维使状态更详细。

循环边界可以直接从0到2M,见版本1。

我写的两个循环边界是:for(j = M+i*1000-1000;j>=0;j--)和for(j = M-i*1000+1000;j<=M*2;j++)。又参考了http://blog.csdn.net/qinmusiyan/article/details/8028627的思路后缩小了边界范围。见版本2。

版本1:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <cstdlib>
using namespace std;
#define clc(s,t) memset(s,t,sizeof(s))
#define INF 0x3fffffff
#define N 100000
int a[105],b[105];
int dp[(N<<1)+5];
int n;
int main(){
    int i,j,res=0;
    scanf("%d",&n);
    for(i = 1;i<=n;i++)
        scanf("%d %d",&a[i],&b[i]);
    for(i = 0;i<=2*N;i++)
        dp[i] = -INF;
    dp[N] = 0;
    for(i = 1;i<=n;i++){
        if(a[i] >= 0){
            for(j = 2*N-a[i];j>=0;j--)
                if(dp[j] != -INF)
                    dp[j+a[i]] = max(dp[j+a[i]],dp[j]+b[i]);
        }else{
            for(j = -a[i];j<=2*N;j++)
                if(dp[j] != -INF)
                    dp[j+a[i]] = max(dp[j+a[i]],dp[j]+b[i]);
        }
    }
    for(i = N;i<=2*N;i++)
        if(dp[i]>=0)
            res = max(res,i+dp[i]-N);
    printf("%d\n",res);
    return 0;
}


版本2:

#include <stdio.h>
#include <string.h>
#define max(a,b) ((a)>(b)?(a):(b))
#define INF 0x3fffffff
#define N 105
#define M 100000
int n,m;
int dp[M*2+5],s[N],f[N];
int main(){
	//freopen("a.txt","r",stdin);
	while(scanf("%d",&n)!=EOF){
		int i,j,res=0,high,low;
		high = low = M;
		for(i = 1;i<=n;i++)
			scanf("%d %d",&s[i],&f[i]);
		for(i = 0;i<=M*2;i++)
			dp[i] = -INF;
		dp[M] = 0;
		for(i = 1;i<=n;i++){
			if(s[i] >= 0){
				for(j = high;j>=low;j--)
					dp[j+s[i]] = max(dp[j+s[i]],dp[j]+f[i]);
				high += s[i];
			}
			else{
				for(j = low;j<=high;j++)
					dp[j+s[i]] = max(dp[j+s[i]],dp[j]+f[i]);
				low += s[i];
			}
		}
		for(i = M;i<=M*2;i++)
			if(dp[i]>=0)
				res = max(res,i-M+dp[i]);
		printf("%d\n",res);
	}
	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值