POJ 2184 Cow Exhibition ( 01背包变形 )

7 篇文章 0 订阅

题意:

牛类想要向世人证明他们聪明又幽默。经过测试,每头牛都有一个幽默度Fi和智商Si,现要求从N头牛中选择若干头牛去参加比赛,假设这若干头牛的智商之和为sumS,幽默度之和为sumF.现要求在所有选择中,在使得sumS>=0&&sumF>=0的基础上,使得sumS+sumF最大并输出其值.

 

分析:

01背包dp之后把符合要求的最优解统计出来。但是在解01背包的时候遇到一个问题是体积有负数,这样在dp的过程中会遇到两个问题:循环的时候超出体积的范围;

压缩空间的时候状态转移方程:dp[v]=max(dp[v],dp[v-c[i]]+w[i]),c[i]为负数时v-c[i]>v,这样按一般的循环的方向从大到下会重复计算。

先看第二个问题,在一般的01背包压缩空间的时候,体积的遍历是从大到小,因为dp[v]=max(dp[v],dp[v-c[i]]+w[i]),当前的dp[v]只取决于比自己小的dp[v-c[i]],所以从大到小遍历时每次dp[v-c[i]]和dp[v]都是上一次的状态。

如果体积为负v-c[i]>v,从大到小遍历dp[v-c[i]]是当前物品的状态,不是上一个,这样就会出错,解决的办法是从小到大遍历。

针对第一个问题,在处理的时候将整个数轴平移,使得原来所有可能的情况都为正。

例如这题,首先计算出数据的范围:

一共100组数,从-1000到1000,那么体积的范围就是-100*1000到100*1000。平移之后我们要处理的数据范围就在0到200000,新的原点变成100000。


#include <limits.h>
#define V 200*1010
#define N 105
#define max(a,b) ((a)>(b)?(a):(b))
int cost[N] ;
int value[N] ;
int dp[V+5] ;
void DP_Solve ( int const n ) ;
void Init_DP ( ) ;
 
int
main ( )
{
    int n ;
    scanf ("%d" , & n ) ;
    int i ;
    for ( i = 1 ; i <= n ; i ++ )
    {
        scanf ("%d%d" , & cost[i] , & value[i] ) ;
    }
    Init_DP ( ) ;
    DP_Solve ( n ) ;
    return 0 ;
}

void Init_DP ( )
{
    int i ;
    for ( i = 0 ; i <= V ; i ++ )
    {
        dp[i] = INT_MIN ;
    }
    dp[100*1000] = 0 ;
}
 
 
void DP_Solve ( int const n )
{
    int i ;
    for ( i = 1 ; i <= n ; i ++ )
    {
        if ( cost[i] > 0 )
        {
            int j ;
            for ( j = V ; j >= cost[i] ; j -- )
            {
                if ( dp[j-cost[i]] > INT_MIN )
                {
                   dp[j] = max ( dp[j] , dp[j-cost[i]] + value[i] ) ;
                }
            }
        }
        else
        {
            int j ;
            for ( j = cost[i] ; j - cost[i] < V  ; j ++ )
            {
                if ( dp[j-cost[i]] > INT_MIN )
                {
                   dp[j] = max ( dp[j] , dp[j-cost[i]] + value[i] ) ;
                }
            }
        }
    }
    int m_max ;
    m_max = 0 ;
    for ( i = 100 * 1000 ; i <= V ; i ++ )
    {
      if ( dp[i] >= 0 )             //注意不能写成 if (dp[i]) 会导致INT_MIN + i - 100 * 1000 溢出
      {
          m_max = max ( m_max , dp[i] + i - 100 * 1000 ) ;
      }
    }
    printf ("%dn" , m_max ) ;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值