動態規划算法1——背包問題


本文作者frankchenfu,blogs網址http://www.cnblogs.com/frankchenfu/,轉載請保留此文字。

動態規划是一個OI選手都熟悉的算法,同時也是剛接觸時比較難理解的。

今天,我為大家分享一類比較簡單的動態規划問題——背包問題

背包問題(Knapsack problem)是在1978年提出的,它都可以類似的描述為:給定一組物品,每種物品都有自己的重量和價格,在限定的總重量內,我們如何選擇,才能使得物品的總價格最高。

背包問題主要有三種分支:

1、01背包;

2、完全背包;

3、多重背包。

其中01背包比較容易,(甚至可以用貪心來做,但這里不予考慮)。

它是這樣的:給定一組物品,每種物品都有自己的重量和價格(“性價比”一般不相同),但是都只可以選一個(一個物品選一次)。在限定的總重量內,我們如何選擇,才能使得物品的總價格最高。

因為只能選一次,所以我們可以先枚舉第i個物品是否能加入背包,然后如何判斷能不能加入呢?在循環里再嵌套一層循環枚舉當前背包已經使用的空間,如果這個空間比當前物品的重量還小肯定不對,就不合算了。每次枚舉都要進行一次決策,取最優值。需要注意的是,這個空間j的枚舉必須從后往前枚舉,不然就可能會取多個相同的物品,顯然這不行,違反了原意(想一想,為什么?)。

好了,應該大家到這里都大概聽懂了吧?如果沒聽懂,也沒關系,可以反復地思考,或者參考其他的文章。如果你的代碼能力比較強,也可以嘗試閱讀代碼,說不定有所幫助。為了降低代碼的空間復雜度,我們對決策數組f[ ]進行了壓維,使它降成了一維。Cpp代碼如下(復制代碼請點圖標):

 1 /*
 2 01背包  3 適用於輸入格式如下的問題,出現問題請自行調整:  4 第一行兩個整數M, N分別表示背包空間與物品總數;  5 第2到N+1行每行兩個整數,分別表示這類物品每個的價值和所需空間。  6 */
 7 #include<cstdio>
 8 const int MAXM=10001,MAXN=51;  9 int m,n; 10 int w[MAXN],c[MAXN]; 11 int f[MAXM]; 12 int max(int x,int y) 13 { 14     return x>y?x:y; 15 } 16 int main() 17 { 18     scanf("%d%d",&m,&n); 19     for(int i=1;i<=n;i++) 20         scanf("%d%d",&w[i],&c[i]); 21     for(int i=1;i<=n;i++) 22         for(int j=m;j>=w[i];j--) 23             f[j]=max(f[j],f[j-w[i]]+c[i]); 24     printf("%d\n",f[m]); 25     return 0; 26 }

那么,我們接下來講的就是完全背包。完全背包和01背包類似,只不過每種物品有無限多件,你愛取幾件取幾件!

它們的代碼也是極其的相似!僅僅是for循環的順序從逆序變成了順序。(剛才說的取多件,其實是變成了完全背包)。Cpp代碼:

 1 /*
 2 完全背包,輸入規則:  3 第一行兩個整數M, N分別表示背包空間與物品總數;  4 第2到N+1行每行兩個整數,分別表示這類物品每個的價值和所需空間。  5 */
 6 #include<cstdio>
 7 const int MAXM=10001,MAXN=51;  8 int m,n;  9 int w[MAXN],c[MAXN]; 10 int f[MAXM]; 11 int max(int x,int y) 12 { 13     return x>y?x:y; 14 } 15 int main() 16 { 17     scanf("%d%d",&m,&n); 18     for(int i=1;i<=n;i++) 19         scanf("%d%d",&w[i],&c[i]); 20     for(int i=1;i<=n;i++) 21         for(int j=w[i];j<=m;j++) 22             f[j]=max(f[j],f[j-w[i]]+c[i]); 23     printf("%d\n",f[m]); 24     return 0; 25 }

 

最后一個最最最難的多重背包,它是每個物品都有有限多個!

它需要用到二進制思想進行拆分(其實有點像拆分成多個01背包,然后再把答案合並)!Cpp代碼如下:

 1 /*
 2 多重背包,輸入格式:  3 第一行,一個整數n,物品數量;  4 第二行,n個整數,第i個整數表示第i個物品的價格bi;  5 第三行,n個整數,第i個整數表示第i個物品的數量ci;  6 第四行,一個整數m,背包空間。  7 */
 8 #include<cstdio>
 9 const int MAXM=10001,MAXN=6001; 10 int v[MAXM],w[MAXM]; 11 int f[MAXN]; 12 int n,m,p; 13 int max(int x,int y) 14 { 15     return x>y?x:y; 16 } 17 int main() 18 { 19     scanf("%d%d",&n,&m); 20     for(int i=1;i<=n;i++) 21  { 22         int x,y,s,t=1; 23         scanf("%d%d%d",&x,&y,&s); 24         for(;s>=t;t<<=1) 25  { 26             v[++p]=x*t; 27             w[p]=y*t; 28             s-=t; 29  } 30         v[++p]=x*s; 31         w[p]=y*s; 32  } 33     for(int i=1;i<=p;i++) 34         for(int j=m;j>=v[i];j--) 35             f[j]=max(f[j],f[j-v[i]]+w[i]); 36     printf("%d\n",f[m]); 37     return 0; 38 }

 

這篇文章到此就結束了。希望對大家有所幫助,謝謝!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM