Time Limit: 1000MS | Memory Limit: 65536K | |
Total Submissions: 10882 | Accepted: 4309 |
Description
Input
Output
Sample Input
5 -5 7 8 -6 6 -3 2 1 -8 -5
Sample Output
8
Hint
今天遇到一题poj2184,大概思路是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。初始化变成:
题意:题目要求选出一些牛,使smartness和funness值的和最大,而这些牛有些smartness或funness的值是负的,还要求最终的smartness之和以及funness之和不能为负。
附上代码:
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 using namespace std; 5 #define S 100000 6 #define M 200000 7 #define INF 0x3f3f3f3f 8 int dp[M+5]; 9 int max(int a,int b) 10 { 11 return a>b?a:b; 12 } 13 int main() 14 { 15 int n,m,i,j; 16 int a[105],b[105]; 17 while(~scanf("%d",&n)) 18 { 19 for(i=0; i<n; i++) 20 scanf("%d%d",&a[i],&b[i]); 21 memset(dp,-INF,sizeof(dp)); //初始化为极小值 22 dp[S]=0; //100000代替0,去除负数 23 for(i=0; i<n; i++) 24 { 25 if(a[i]>0) //大于0,从右到左常规推导 26 { 27 for(j=M; j>=a[i]; j--) 28 if(dp[j-a[i]]>-INF) 29 dp[j]=max(dp[j],dp[j-a[i]]+b[i]); 30 } 31 else //小于0,则需要从左到右推导 32 { 33 for(j=0; j-a[i]<=M; j++) 34 if(dp[j-a[i]]>-INF) 35 dp[j]=max(dp[j],dp[j-a[i]]+b[i]); 36 } 37 } 38 int sum=0; 39 for(i=S; i<=M; i++) 40 if(dp[i]>=0) //都必须大于0 41 sum=max(sum,dp[i]+i-S); 42 printf("%d\n",sum); 43 } 44 return 0; 45 }
原文:http://www.cnblogs.com/pshw/p/5031109.html