2184 cow exhibition

題意:給出N組數,每一組有Si和Fi兩個數,從中選出若干組,使得Si的和TS與Fi的和TF相加最大,並且TS和TF都必須大於等於0。Si和Fi從-1000到1000。

分析:這又是一道典型的揹包問題。我們這麼想,設dp[i][j]是前i組數的TS爲j時,TF的最大值。這樣就把TS當成了體積,把TF當成了價值。只不過體積也是會不斷變化的,不確定到底體積是多少,但一定是大於等於0的。因爲Si從-1000到1000,所以TS的最小值就是-1000*100=-100000,因爲揹包問題需要參數都是正數,所以我們可以加一個100000的偏移。這樣0對應-100000,100000對應0,200000對應100000。到最後求TS+TF時只需要求j-100000+dp[j]的最大值就可以了。這樣就轉化爲01揹包的思想。

但是需要注意的是,對於01揹包來講,應該用逆向循環來保證每個物品只被取一次,但是,對於負數的情況,如果從後向前循環,就會重複利用到當前物品已經被取的結果,變成完全揹包,所以,負數的時候,應該從前往後順序循環。即若轉化爲一維數組的01揹包,爲防止重複相加,當前Si<0時應該從左往右掃,Si>0時從右往左掃。再詳細說一下:

在一般的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]]是當前物品的狀態,不是上一個,這樣就會出錯,解決的辦法是從小到大遍歷。

 

見代碼:

#include <iostream>
using namespace std;
#define M 100000
#define MIN -999999
typedef struct _COW {
	int s;
	int f;
}COW;
COW cow[101];
int dp[2*M+1];

int max(int a, int b)
{
	return a>b?a:b;
}

int main(int argc, char **argv)
{
	int N;
	while (cin>>N) {
		for (int i=1; i<=N; ++i) {
			cin>>cow[i].s>>cow[i].f;
		}

		for (int i=0; i<=2*M; ++i)
			dp[i] = MIN;
		dp[M] = 0;//初始化,前0組數中取若干組,TS爲0時,TF自然也是0.

		for (int i=1; i<=N; ++i) {
			if (cow[i].s<0 && cow[i].f<0)//剪枝
				continue;
			if (cow[i].s>0) {
				for (int j=2*M; j>=cow[i].s; --j)//0-1揹包了,注意從大到小
					if(dp[j-cow[i].s]>MIN)//剪枝
						dp[j]=max(dp[j-cow[i].s]+cow[i].f,dp[j]);
			} else {
				for (int j=cow[i].s; j-cow[i].s<=2*M; ++j)//0-1揹包,從小到大
					if(dp[j-cow[i].s]>MIN)//剪枝
						dp[j]=max(dp[j-cow[i].s]+cow[i].f,dp[j]);
			} 
		}

		int ans=MIN;
		for(int i=M;i<=2*M;i++)//剪掉了i<M的情況,即相對值i<0的情況;
		{
			if(dp[i]>=0)//這個一定要大於等於,因爲fi可以爲零 
				ans=max(ans,dp[i]+i-M);
		}
		cout<<ans<<endl;

	}
	
	system("pause");
	return 0;
}

172MS過!done!


發佈了29 篇原創文章 · 獲贊 46 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章