POJ 2184 Cow Exhibition(01背包变形)

此题想了很久来着,因为是看背包专题做的题,后面提示是把其中一个量作为物品大小,另一个作为价值。我照这个思路把题目敲完了。因为有负值我想了2个思路,一个是把每一个都加上1000,然后就按照普通的01背包来做,但是这样会有一个问题,到时候扫描一遍找最大值的时候不知道选了几个,也就不知道减几个1000,是不是可以用加一个保存选个数的数组,这样也不行,因为如果 

dp[j] = dp[j - a[ i ]]+b[i],到底是要前者还是后者?也许是要看j - 要减的1000是不是正的?我觉得不靠谱,于是思路否决。。

第二个思路就很简单了,直接给背包加一个值mid=n*1000,这样背包就不会有负值了,但是递推的时候有个问题,如果用1维的,负数的时候跟它产生联系的是后面的不是前面的,就需要正推了,解决方案是分情况,其实还可以用滚动数组,直接推上一层的就不用讨论了,不过问题在于数组之间复制的时候耗费赶脚很长。。因为很大。

全部推完后,dp[j]代表容量为j的背包(可达到的)所获得的最大价值,也就是说聪明值为j最大可以有dp[ j ]的有趣值,这样只要从最大的扫描到mid,找出这2个值加起来(要减mid)的最大就是答案了。

做完这题后我有点想法,DP问题就是要找子问题,子问题如果推不出最终解或者得不到下一个子问题就是错误的,比如此题,如果想找聪明值和有趣值的和最大作为子问题肯定不行,因为题目说2者都要为正。要是一个1000,-50,另一个1000,-1,你就要把2个都选上了。因为2个的和都很大,但是其他的奶牛不一定能把有趣值变为正的,所以就是错误的子问题。


AC代码:

#include<cstdio>
#include<ctype.h>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<vector>
#include<stack>
#include<cmath>
#include<queue>
#include<set>
#include<ctime>
using namespace std;
#define NMAX 100000
#define ll long long
int dp[200005];
int s[105],f[105];
int main()
{
//    freopen("input.txt","r",stdin);
//    freopen("o1.txt","w",stdout);
    int i,j,k,n;
    while(~scanf("%d",&n))
    {
        int t1,t2;
        for(k = i = 0; i < n; i++)
        {
            scanf("%d%d",&t1,&t2);
            if(t1>=0 || t2>=0)
            {
                s[k] = t1;
                f[k++] = t2;
            }
        }
        int mid = k*1000,v = 2*mid;
        for(i = 0; i <= v; i++) dp[i] = (i==mid)?0:-NMAX;
        for(i = 0; i < k; i++)
        {
            if(s[i]>0)
            {
                for(j = v; j >= s[i]; j--)
                    if(dp[j-s[i]] != -NMAX)
                        dp[j] = max(dp[j],dp[j-s[i]]+f[i]);
            }
            else
            {
                for(j = 0; j <= v+s[i]; j++)
                    if(dp[j-s[i]] != -NMAX)
                        dp[j] = max(dp[j],dp[j-s[i]]+f[i]);
            }
        }
        int ans = -1;
        for(i = v; i >= mid; i--)
            if(dp[i] >= 0 && dp[i]+i-mid > ans)
                ans = dp[i]+i-mid;
        printf("%d\n",ans);
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值