1. 程式人生 > >NOI題庫--砝碼稱重V2(多重揹包2^n拆分)

NOI題庫--砝碼稱重V2(多重揹包2^n拆分)

以前只會寫多重揹包的原版,渣的不行,為了做此題不得不學習了一下,發現其實也不難,只要理解了方法就好多了(PS:其實和倍增挺像的)

8756:砝碼稱重V2
總時間限制: 1000ms 記憶體限制: 65536kB
描述
設有1g、2g、3g、5g、10g、20g的砝碼各若干枚(其總重<=100,000),要求:計算用這些砝碼能稱出的不同重量的個數,但不包括一個砝碼也不用的情況。
輸入
一行,包括六個正整數a1,a2,a3,a4,a5,a6,表示1g砝碼有a1個,2g砝碼有a2個,……,20g砝碼有a6個。相鄰兩個整數之間用單個空格隔開。
輸出
以“Total=N”的形式輸出,其中N為可以稱出的不同重量的個數。
樣例輸入


1 1 0 0 0 0
樣例輸出
Total=3
提示
樣例給出的砝碼可以稱出1g,2g,3g三種不同的重量。

多重揹包的2^n拆分,就是把一種多份的物體分解成幾種價值高的物體來進行01揹包就好,舉個栗子:
**對於數量為 n 的同種物品 k 價值為 v
可以拆分打包為 1 , 2 , 4 , 8 , 16 …… ,2^x , n的剩餘數量
價值為 v , 2*v ……
例如 32 個 價值為 2 的物品可以拆為
1個價值為 1*2 的物品,
1個價值為 2*2 的物品,
1個價值為 4*2 的物品,
1個價值為 8*2 的物品,
1個價值為 16*2 的物品,
1個價值為 31*2 的物品,
用這6個物品可以組合出原先32個物品的所有狀態**

下面是程式碼:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm> 
using namespace std;
int a[1000100]={0};
int f[1000100]={0};
int zz=0;
int value[7]={0,1,2,3,5,10,20};
int fm[7]={0};
int sum=0;
int total=0;
const int cf[] = {1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384
,32768,65536,131072}; //以二為底的指數為i的值 void fj(int ki,int valuei)//二進位制分解的過程 { int i=0; while (cf[i] <= ki) { a[++zz]=cf[i]*valuei;//zz是一個指標,指向當前地址 ki-=cf[i]; i++; } if (ki > 0) a[++zz]=ki*valuei; } int main() { int i,j; for (i=1;i<=6;i++) { scanf("%d",&fm[i]); sum+=fm[i]*value[i];//最高價值 fj(fm[i],value[i]); } f[0]=1; for (i=1;i<=zz;i++) for (j=sum; j>=a[i];j--) f[j]=f[j] || f[j - a[i]];//只是一個普通的01揹包 for (i=1;i<=sum;i++) if (f[i]) total++; printf("%s%d","Total=",total); return 0; }

“`