1. 程式人生 > >HDU1114 完全揹包問題

HDU1114 完全揹包問題

瑪德,陣列開小了會超時。。。。

這篇文章就記錄一下自己對於01揹包到完全揹包一維陣列轉換的理解。推薦去看《揹包九講》

as we all know,01揹包的二維陣列的轉移方程就是:(這個真的是超級重要,後面的其他揹包問題都可以看作是01揹包問題延申出來的。嗯,我是個渣渣~我是看網上的大佬都這樣說)

for(int i=0;i<n;i++){
	for(int j=0;j<=M;j++){
		if(w[i]<=j)	  dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+v[i])
		else   		  dp[i][j]=max(dp[i-1][j],dp[i][j]);	
	}
};

時間複雜度不能再優化了,只能優化空間複雜度:

我們的二維陣列是怎樣實現的呢?外面大迴圈for i 0—n(有n個揹包),裡面是for j 0—M(M是最大的容量)。然後,通過遍歷每一個揹包來更新dp。

那麼一維陣列dp[i]  for i 0—M,dp[i]能不能根據記錄dp[i-1]更新的資料再進行更新呢?dp[i-1]的資料怎樣儲存呢?

for(int i=0;i<n;i++){
	for(int j=M;j>=0;j--){
		if(w[i]<=j){
			dp[j]=max(dp[i],dp[i-w[i]]+v[i]);
		}
	}
}

首先最外層肯定是一個大迴圈,但是裡面的質量就要倒敘遍歷for j M—0,為什莫要這樣呢?

假設是正序遍歷,for i 0—M,給下列資料:

m: 5

w:2        1     1

v:100     2     3

如果是順序遍歷的話,2—100的這個揹包會被重複放進去,反序的話,剛好可以。

(ps:前面居然會出現重複的情況,呀,Do you konw 完全揹包?完全揹包就是就是在01揹包的基礎上,再加上一個條件:每一個揹包都是可以無限次重複。那這個順序的一位陣列解法不就剛好適用於完全揹包的解法嘛,居然陰差陽錯的湊成了完全揹包的解法,hha,其實記住轉移方程的最好的解法還是記住推導的過程)

一維陣列dp[j]中表示的就是二維陣列中我們定義的狀態dp[i][j],dp[j-w[i]]就是原來的dp[i][j-w[i]]。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int maxn=100000+10;
int v[maxn];
int w[maxn];
int dp[maxn];
#define inf 0x3f3f3f3f
int main()
{
	int T;
	cin >> T;
	while(T--)
	{
		int n,m;
		cin >> n >> m;
		m=m-n;
		int cnt,i,j;
		cin >> cnt;
		for(i=0;i<cnt;i++){
			cin >> v[i] >> w[i];
		}
		memset(dp,inf,sizeof(dp));
		dp[0]=0;
		for(i=0;i<cnt;i++){
			for(j=w[i];j<=m;j++){
				dp[j]=min(dp[j],dp[j-w[i]]+v[i]);
			}
		}
		if(inf==dp[m]){
			printf("This is impossible.\n");
		}else{
			cout << "The minimum amount of money in the piggy-bank is " << dp[m] << "." << endl;
		}
	}		
	
	return 0;
}