題意:將價值分別為1,2,3,4,5,6的6種多個物品平均分兩坨
所有物品數量加起來不超過2w個
很容易想到dp的思路,dp[i]表示是否存在使價值為i的選法,dp[i]=dp[i-j] j為物品的價值。然後對所有物品進行這種迴圈即可,但是會超時,這時候就需要將大量的相同物品二進位制分割,具體的原理需要用心去感受,這是揹包九講中的原文:
方法是:將第i種物品分成若干件物品,其中每件物品有乙個係數,這件物品的費用和價值均是原來的費用和價值乘以這個係數。使這些係數分別為 1,2,4,…,2^(k-1),n[i]-2^k+1,且k是滿足n[i]-2^k+1>0的最大整數。例如,如果n[i]為13,就將這種 物品分成係數分別為1,2,4,6的四件物品。揹包的上限空間是總價值/2 ,背完後看看dp[總價值/2]是不是true即可。分成的這幾件物品的係數和為n[i],表明不可能取多於n[i]件的第i種物品。另外這種方法也能保證對於0..n[i]間的每乙個整數,均可以用若干個係數的和表示,這個證明可以分0..2^k-1和2^k..n[i]兩段來分別討論得出,並不難,希望你自己思考嘗試一下。
這樣就將第i種物品分成了o(log n[i])種物品,將原問題轉化為了複雜度為o(v*σlog n[i])的01揹包問題,是很大的改進。
下面給出o(log amount)時間處理一件多重揹包中物品的過程,其中amount表示物品的數量:
希望你仔細體會這個偽**,如果不太理解的話,不妨翻譯成程式**以後,單步執行幾次,或者頭腦加紙》筆模擬一下,也許就會慢慢理解了。procedure
multiplepack
(cost,weight,amount)
ifcost*amount>=v
completepack
(cost,weight)
return
integer
k=1 while
kzeroonepack
(k*cost,k*weight)
amount=amount-k
k=k*2
zeroonepack
(amount*cost,amount*weight)
貼**。value開100是因為經過二進位制分割之後粗略算一下不會有超過100個物品。
#include
#include
#include
#include
#include
#include
#include
#include
#define ninf int_min
#define ll long long
using
namespace
std;
//用多重揹包的方法,將一坨一樣的珠寶變成乙個乙個不一樣的
int value[100];
int total;
int totalvalue;
bool dp[61111];
//dp[i]為true表示存在分出i的價值的方法
int main()
if (tmp)
value[total++]=tmp*i;
}if (totalvalue&1)
//要求剛好分一半 即揹包剛好裝滿,對於最開始的狀態,沒有乙個物品的情況下除了0空間之外其他都是非法的
memset(dp,0,sizeof(dp));
dp[0]=true;
for (int i=1;i//i是選擇的物品
for (int j=totalvalue/2;j>=value[i];j--) //j是空間
if (dp[j-value[i]])
dp[j]=true;
if (dp[totalvalue/2])
cout
<<"can be divided."
cout
<<"can't be divided."
0;}
HDU 1059 Dividing(多重揹包)
題意 輸入6個數字,表示重量分別為1 2 3 4 5 6的大理石的數量 問能不能將這些大理石均分,不能把大理石弄破了 能的話就輸出 can be divided.不能就輸出can t be divided.解題思路 邊輸入邊求和 如果和為0,就break 如果和為奇數,就輸出can t be div...
HDU 1059 Dividing 多重揹包
題目就是要分彈珠,一人一半,多乙個少乙個都不行。六種彈珠,ni表示價值i的彈珠的數量,問能不能分。1.總價值為奇數當然不能分了。2.總價值為偶數,利用多重揹包求解,揹包容量為m 2,計算dp m 能否等於m,能就可以分。include includeint val 10 num 10 int dp ...
hdu 1059 Dividing 多重揹包
傳送門 分析 多重揹包按照二進位制拆分為01揹包 假設價值為w的物品有n個,則按照 1,2,4,8,1 log n 拆分 設sum 1,1 log n sum,設 ret n sum 則有在 0 sum 內的所有數p都可以用拆分數的和表示 二進位制表示 而 sum n 之間的數可用 p ret表示 ...