动态规划之背包问题(C语言)
動態規劃
動態規劃(英語:Dynamic programming,簡稱DP)是一種通過把原問題分解為相對簡單的子問題的方式求解復雜問題的方法。
動態規劃常常適用于有重疊子問題和最優子結構性質的問題
動態規劃思想大致上為:若要解一個給定問題,我們需要解其不同部分(即子問題),再合并子問題的解以得出原問題的解。
由于通常許多子問題非常相似,為此動態規劃法試圖僅僅解決每個子問題一次,從而減少計算量:一旦某個給定子問題的解已經算出,則將其記憶化存儲,以便下次需要同一個子問題解之時直接查表。這種做法在重復子問題的數目關于輸入的規模呈指數增長時特別有用。
背包問題
背包問題可以描述為:給定一組物品,每種物品都有自己的重量和價格,在限定的總重量內,如何選擇才能使得物品的總價格最高。
背包問題是典型的動態規劃問題。
而背包問題還存在需要恰好裝滿背包和不需要恰好裝滿兩種情況
這篇文章所探討的所有背包問題都是建立在不需要恰好裝滿的情況下
由簡單背包問題的基礎上又衍生出許多問題都可以采用動態規劃解決。
例如:
1. 01背包問題(每種物品只有一件,放或者不放)
2. 完全背包問題(每件物品有無限件可用)
3. 多重背包問題(每件物品有n[i]件可用)
01背包問題
題目:有N件物品和一個容量為V的背包。第i件物品的費用是weight[i],價值是value[i]。求將哪些物品裝入背包可使價值總和最大。
01背包問題是最基礎的背包問題,”01”的意思是:每種物品僅有一件,放為“1”,不放為“0”。
我們假定f[i][v]為將前i件物品前恰好放入一個容量為V的背包中可獲得的最大價值
則其狀態轉移方程是:
f[i][V]=max{f[i-1][V],f[i-1][V-weight[i]]+value[i]}
由上圖可知,背包問題可以簡化為“將前i件物品放入容量為V的背包中”的問題,而這個問題可以優化為,“不放第i件物品“和“放第i件物品“的問題。
如果不放第i件物品,問題為將前i-1件物品放入容量為V的背包中,總價值為f[i-1][v]
如果放第i件物品,問題為前i-1件物品放入剩下的容量為V-weight[i]的背包中,價值為f[i-1][V-weight[i]]+value[i]
代碼部分較為簡單,主要學習動態規劃這種思想:
該算法的時間復雜度一定降到最低,但是我們還可以通過壓縮空間復雜度的方式優化代碼,方法就是將二維數組f[i][j]轉化為一維數組[j]只需將完整程序稍作修改即可,這里只給出核心代碼。
for(int i = 1; i <= N; ++i)for(int j = V; j >= weight[i]; --j)f[j] = max(f[j], f[j - weight[i]] + value[i]);具體代碼如下:
#include<stdio.h> #define V 1500 int f[10][V];//全局變量,自動初始化為0 int weight[10]; int value[10]; #define max(x,y) (x)>(y)?(x):(y) int main() { //狀態轉移方程 f[i+1][j]=max{f[i][j],f[i][j-weight[i+1]+value[i+1]}int N,M; freopen("1.txt","r",stdin); scanf("%d%d",&N,&M);//N物品個數 M背包容量 for (int i=1;i<=N; i++) { scanf("%d%d",&weight[i],&value[i]); } //動態規劃for (int i=1; i<=N; i++) for (int j=1; j<=M; j++) { if (weight[i]<=j) { f[i][j]=max(f[i-1][j],f[i-1][j-weight[i]]+value[i]); } else f[i][j]=f[i-1][j]; } printf("%d\n",f[N][M]);//輸出最優解}到這里已經基本實現01背包問題,但是該程序在輸出的時候,只能輸出最后的價值,不能知道選擇的物品是哪個。在這里我們定義一個數組x[i],對于每一個物品,如果被選擇置為“1”,否則為“0”。通過這樣的方式來實現。
修改之后的代碼如下:
完全背包問題
題目:有N件物品和一個容量為V的背包。第i件物品的費用是weight[i],價值是value[i]。每件物品可以無限選用,求將哪些物品裝入背包可使價值總和最大。
完全背包問題不設定物品取用上限
對于算法的優化我們可以這樣想:
在01背包問題中,我們要保證第i次循環中的f[i][v]是由f[i-1][V-weight[i]]遞推而來,每一次都是“加選出一個(即一種)物品”而這種方式同時也保證了每件物品只選一次。
而完全背包問題的特點剛好是每種物品可選無限件,所以在考慮“加選出一個(即一種)物品”時就是單純的考慮“加選出一個(可能為同一種)物品”,這樣我們就需要考慮選入的物品是已經選入的情況。相比來說,反而簡化了代碼。
同樣,我們假定f[i][v]為將前i件物品前恰好放入一個容量為V的背包中可獲得的最大價值
則其狀態轉移方程是:
f[i][V]=max{f[i-1][V],f[i-1][V-k*weight[i]]+k*value[i]}
0<=k*weight[i]<=v,其中0<=k<=V/weight[i+1]
該狀態方程等價于f[i][v]=max{f[i-1][v],f[i-1][V-weight[i]]+value[i]}
代碼部分如下:
一維數組優化:
for(int i = 1; i <= N; ++i)for(int j = weight[i]; j <= V; ++j)f[j] = max(f[j], f[j - weight[i]] + value[i]);具體代碼如下:
#include<stdio.h> #define V 1500 int f[10][V];//全局變量,自動初始化為0 int weight[10]; int value[10]; #define max(x,y) (x)>(y)?(x):(y) int main() { //f[i][v]=max{f[i-1][v-k*c[i]]+k*w[i]|0<=k*c[i]<=v},其中0<=k<=V/weight[i+1]//f[j]=max(f[j],f[j-weight[i]]+value[i]) int N,M; //初始化freopen("1.txt","r",stdin); scanf("%d%d",&N,&M);//N物品個數 M背包容量 for (int i=1;i<=N; i++) { scanf("%d%d",&weight[i],&value[i]); } //動態規劃for(int i = 1; i <= N; ++i)for(int j = 0; j <= M; ++j)for(int k = 0; k * weight[i] <= j; ++k)f[i][j] = max(f[i][j], f[i - 1][j - k * weight[i]] + k * value[i]);printf("%d",f[N][M]);//輸出最優解 }多重背包問題
題目:有N件物品和一個容量為V的背包。第i件物品最多有n[i]個,每個的費用是weight[i],價值是value[i]。每件物品最多可以選用相應的最大個數,求將哪些物品裝入背包可使價值總和最大。
多重背包問題設定物品選擇上限
代碼部分如下:
一維數組優化:
for(int i = 1; i <= N; ++i)for(int j = V; j >= 0; --j)for(int k = 1; k <= num[i] && k * weight[i] <= j; ++k)f[j] = max(f[j], f[j - k * weight[i]] + k * value[i]);具體代碼如下:
#include<stdio.h> #define V 1500 int f[10][V];//全局變量,自動初始化為0 int weight[10]; int value[10]; int num[10]; #define max(x,y) (x)>(y)?(x):(y) int main() { //f[i][v]=max{f[i-1][v-k*c[i]]+k*w[i]|0<=k*c[i]<=v},其中0<=k<=V/weight[i+1]//f[j]=max(f[j],f[j-weight[i]]+value[i]) int N,M,cur; freopen("2.txt","r",stdin); scanf("%d%d",&N,&M);//N物品個數 M背包容量 for (int i=1;i<=N; i++) { scanf("%d%d%d",&weight[i],&value[i],&num[i]); } for(int i = 1; i <= N; ++i)for(int j = 0; j <= V; ++j)for(int k = 0; k <= num[i] && k * weight[i] <= j; ++k)f[i][j] = max(f[i-1][j], f[i - 1][j - k * weight[i]] + k * value[i]);printf("%d\n",f[N][M]);//輸出最優解 }注:由于測試輸入數據較多,程序可以采用文件輸入
5 10
2 6
2 3
6 5
5 4
4 6
多重背包需要輸入每件物品的可選用次數,所以它的輸入文件有所不同
5 10
2 6 1
2 3 1
6 5 1
5 4 1
4 6 1
之所以將次數都定為1,是說明01背包問題是多重背包的一種特殊情況。
總結
以上是生活随笔為你收集整理的动态规划之背包问题(C语言)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: centos7安装VLC播放器
- 下一篇: splite和map的结合使用