POJ 2184 Cow Exhibition (01背包变形)(或者搜索)

Cow Exhibition
Time Limit: 1000MS   Memory Limit: 65536K
Total Submissions: 10342   Accepted: 4048

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.

Source

 
两种做法:1 背包  2 DFS
 
 1 背包
1)把Smartness看成花费,把Funness看成价值。
2)因为有负数下标,所以要处理,最大是100*1000也就是100000.记作w所以开一个dp[2*w+5]的数组。w表示0,0到w-1和w+1到2w分别表示负的和正的1到100000.
3)状态转移方程:  dp[j]=max(dp[j],dp[j-s[i]]+f[i]); 但要注意01背包,在s[i]大于0时,从大到小的顺序,可以保证每件物品只取一次,在s[i]小于0时,要从小到大,才能保证每件物品只取一次. (s[i]<0时,j-s[i]>j)
4)最后遍历一下dp[i]+i-w最大的数即可。(i要从w开始!开始从0开始WA了一次)
#include<queue>
#include<math.h>
#include<stdio.h>
#include<string.h>
#include<string>
#include<iostream>
#include<algorithm>
using namespace std;
#define INF 0x3f3f3f3f
#define w 100000

int dp[w*2+5],s[105],f[105];
int n;
int main()
{
    while(cin>>n)
    {
        for(int i=0;i<=2*w;i++)
            dp[i]= -INF;
        dp[w]=0;
        int sum=w;
        for(int i=1;i<=n;i++)
        {
            scanf("%d%d",&s[i],&f[i]);
            if(s[i]>0)sum+=s[i];
        }
        for(int i=1;i<=n;i++)
        {
            if(s[i]<0 && f[i]<0)
                continue;
            else if(s[i]>=0)
            {
                for(int j=sum;j>=s[i];j--)
                    if(dp[j-s[i]]!= -INF)
                        dp[j]=max(dp[j],dp[j-s[i]]+f[i]);
            }
            else
            {
                for(int j=s[i];j<=sum+s[i];j++)
                    if(dp[j-s[i]]!= -INF)
                        dp[j]=max(dp[j],dp[j-s[i]]+f[i]);
            }
        }
        int ans=-INF;
        for(int i=w;i<=sum;i++)
            if(dp[i]>=0)
                ans=max(ans,dp[i]+i-w);

        cout<<ans<<endl;
    }
    return 0;
}
 2 DFS
1)用DFS遍历所有可能,最后sumS+sumF大于之前ans且sumF,sumS均>=0则替换ans
2)最重要的是剪枝,开始数据处理如果s[i],f[i]均小于0,剔除数据,均大于0直接加到sumS和sumF中
3)将s[i]与f[i]的和排序,大的可能是优先会选的,然后用sum数组表示选到第i个时最大不会超过的数,如果这个数都小于之前ans,就不用再走了return(这个是最主要的剪枝)
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
using namespace std;
#define N 105

int s[N],f[N],a[N],sf[N],n,df,ds,ans,sumS,sumF,sum[N];

void dfs(int x)
{
    if(x==n+1)
    {
        if(sumS+sumF>ans && sumS>=0 && sumF>=0)
            ans=sumS+sumF;
        return ;
    }

    if(sf[x]<=0 && sumS+sumF<ans)return;//剪枝(和小于0存在的唯一目的就让前面和比较大但是会出现两个属性中有负数的变成正数,然而如果和还不如之前的ans就没有存在的必要了
    if(sumS+sumF+sum[x]<=ans)return;    //剪枝(主要靠这个,见下)

    sumS+=s[x];
    sumF+=f[x];
    dfs(x+1);
    sumS-=s[x];
    sumF-=f[x];
    dfs(x+1);
}

int main()
{
    while(~scanf("%d",&n))
    {
        sumS=sumF=0;
        for(int i=1;i<=n;i++)
        {
            scanf("%d%d",&s[i],&f[i]);
            if(s[i]<0 && f[i]<0)
            {
                i--,n--;
                continue;
            }
            if(s[i]>=0 && f[i]>=0)
            {
                sumS+=s[i];
                sumF+=f[i];
                i--;n--;
                continue;
            }
            sf[i]=(s[i]+f[i]);
        }

        for(int i=1;i<=n-1;i++)//排序,这部分可以用结构体加sort写,目的让两个值都高的在前面
            for(int j=i+1;j<=n;j++)
            {
                if(sf[i]<sf[j])
                {
                    swap(sf[i],sf[j]);
                    swap(s[i],s[j]);//这个不能忘了
                    swap(f[i],f[j]);//同上
                }
            }

        memset(sum,0,sizeof(sum));
        for(int i=n;i>=1;i--)//sum[i]表示排序之后选到第i头牛时一个大致的数(比实际数还大)
            if(sf[i]<=0) sum[i]=0;//所以如果sumS+sumF加sum[i]都不能大于之前的ans把,那这条路肯定是不行的就return
            else sum[i]=sum[i+1]+sf[i];

        ans=sumS+sumF;
        dfs(1);
        printf("%d
",ans);
    }
    return 0;
}

原文地址:https://www.cnblogs.com/wmxl/p/4753799.html