poj 2184 Cow Exhibition(处理负数的01背包)

今天遇到一题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。

#include
#include
#include
#include
using namespace std;
const int inf=200000;
int dp[inf+10];//100*1000*2,表示当s之和达到i时,f之和的最大值
int w[110],v[110];
int onePack(int n){
	int i,j,s;
	for(i=0;i0){
			for(j=inf;j>=w[i];j--){//从大向小遍历
				dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
			}
		}else{ //从小向大遍历 
			for(j=0;j-w[i]<=inf;j++){
				dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
			}
		}
	}
	for(s=-inf,i=100000;i<=inf;i++){
		if(dp[i]>=0){
			s=max(s,dp[i]+i-100000);
		}
	} 
	return s;
}
int main(){
	int n,m,i,j;
	while(~scanf("%d",&n)){
		memset(w,0,sizeof(w));
		memset(v,0,sizeof(v));
		for(i=0;i<=inf;i++){//初始化 
			dp[i]=-inf; 
		}
		dp[inf/2]=0;
		for(i=0;i


你可能感兴趣的:(DP)