dp_揹包之多重揹包
阿新 • • 發佈:2022-05-19
問題:
多重揹包也是 0-1 揹包的一個變式。與 0-1 揹包的區別在於每種物品有ki個,而非一個。
解決方案:
將k個相同的物品,看作k個不同的物品,但是wi,ci都一樣。即可套用 01揹包方案 詳見(https://www.cnblogs.com/kingbuffalo/p/16241927.html)
優化方法:
- 二進位制優化
設k個物品分成 A[xx] A[xx+1] ... A[xx+k-1] 個物品。
那麼 A[xx] A[xx+1] 組成兩個物品時 與 A[xx+1] A[xx+2] 是相同的,所以存在重複計算。
所以,不能簡單地將其看成k個不同的物品。
而是考慮二進位制 比如將 8 = 1 + 2 + 3 + 1 4個物品,分別為1個的時候,2個的時候,4個的時候,和1個的時候。(這是因為二進位制 剛好就是可以表示一切數字,且某位存不存在-->即加不加上)
poj1014的 參考程式碼如下
#define NMAX 120003 bool dp[NMAX]; int B[10]; int A[1000]; int main(){ ios::sync_with_stdio(false); cin.tie(0); int t = 0; while(cin >> B[1] >> B[2] >> B[3] >> B[4] >> B[5] >> B[6] ){ t ++; int M = B[1] + B[2]*2 + B[3]*3 + B[4]*4 + B[5]*5 +B[6]*6; if ( M == 0 ) break; if ( M & 1 ){ cout << "Collection #"<<t <<":\nCan't be divided.\n" << endl; }else{ M/=2; int AIdx = 0; for(int i=1;i<=6;i++){ int k = B[i]; if ( k == 0 ) continue; int j=1; while(k>=j)A[AIdx++]=j*i,k-=j,j*=2; if(k)A[AIdx++] = k*i; } memset(dp,0,sizeof(dp)); dp[0] = true; for(int i=0;i<AIdx;++i){ for(int j=M;j>=A[i];j--){ dp[j] |= dp[j-A[i]]; } } if ( dp[M] ) cout << "Collection #"<<t <<":\nCan be divided.\n" << endl; else cout << "Collection #"<<t <<":\nCan't be divided.\n" << endl; } } return 0; }
- 還有一種方法,我不知道這個叫什麼方法了
#define NMAX 120003 bool dp[NMAX]; int cnts[NMAX]; int B[10]; int main(){ ios::sync_with_stdio(false); cin.tie(0); int t = 0; while(cin >> B[1] >> B[2] >> B[3] >> B[4] >> B[5] >> B[6] ){ t ++; int M = B[1] + B[2]*2 + B[3]*3 + B[4]*4 + B[5]*5 +B[6]*6; if ( M == 0 ) break; if ( M & 1 ){ cout << "Collection #"<<t <<":\nCan't be divided.\n" << endl; }else{ M/=2; memset(dp,0,sizeof(dp)); dp[0] = true; for(int i=1;i<=6;++i){ memset(cnts,0,sizeof(cnts)); for(int j=i;j<=M;j++){ if ( !dp[j] && dp[j-i] && cnts[j-i] < B[i]){ dp[j] = true; cnts[j] = cnts[j-i]+1; } } } if ( dp[M] ) cout << "Collection #"<<t <<":\nCan be divided.\n" << endl; else cout << "Collection #"<<t <<":\nCan't be divided.\n" << endl; } } return 0; }