HDU1114 完全揹包問題
阿新 • • 發佈:2018-11-08
瑪德,陣列開小了會超時。。。。
這篇文章就記錄一下自己對於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;
}