日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

01背包问题+完全背包问题+多重背包问题

發布時間:2024/10/6 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 01背包问题+完全背包问题+多重背包问题 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一 01背包問題

1.1題目

有N件物品和一個容量為V 的背包。放入第i件物品耗費的空間是Ci,得到 的價值是Wi。

求解將哪些物品裝入背包可使價值總和最大。

1.2 基本思路

這是最基礎的背包問題,特點是:每種物品僅有一件,可以選擇放或不 放。

用子問題定義狀態:即F[i, v]表示前i件物品恰放入一個容量為v的背包可以 獲得的最大價值。

則其狀態轉移方程便是:

F[i, v] = max { F [i ? 1, v], F [i ? 1, v ? Ci ] + Wi }

對于“將前i件物品放入容量為v的背包 2 中”這個子問題,若只考慮第i件物品的策略(放或不放),

那么就可以轉化 為一個只和前i ? 1件物品相關的問題。

如果不放第i件物品,那么問題就轉化 為“前i ? 1件物品放入容量為v的背包中”,價值為F[i ? 1, v];

如果放第i件物 品,那么問題就轉化為“前i ? 1件物品放入剩下的容量為v ? Ci的背包中”,

此時能獲得的最大價值就是F[i ? 1, v ? Ci ]再加上通過放入第i件物品獲得的價 值Wi。

#include<iostream> #include<cstdio> using namespace std; const int maxn = 105; int dp[maxn][maxn]; int c[maxn];//第i件物品耗費的空間是Ci int w[maxn];//得到的價值是Wi。 int main() {int v;//背包容量int n;//N件物品while(scanf("%d %d",&v,&n) != EOF){for(int i = 1;i <= n; i++){scanf("%d",&c[i]);}for(int i = 1;i <= n; i++){scanf("%d",&w[i]);}//動態規劃邊界問題for(int i = 0;i <= n; i++){dp[i][0] = dp[0][i] = 0;}for(int i = 1;i <= n; i++){//枚舉物品for(int j = 1;j <= v; j++){//枚舉背包容量if(j < c[i]){ //當前背包容量放不下這件物品dp[i][j] = dp[i - 1][j];}else{ //當前背包容量可以放下這件物品dp[i][j] = max(dp[i - 1][j],dp[i - 1][j - c[i]] + w[i]);}}}printf("%d\n",dp[n][v]);}return 0; }

1.3 優化空間空間復雜度

以上方法的時間和空間復雜度均為O(V*N),其中時間復雜已經不能再優化,但是空間復雜度可以優化到O(N)。

先考慮上面講的基本思路是如何實現的,肯定是有一個主循環 i = 1....N,每次算出來二維數組f[i][0....v]的所有值;

如果只用一個數組f[0...V],能不能保證第i次循環結束后 f[v]中表示的就是我們定義的狀態 f[i][v]呢?

f[i][v]是由f[i-1][v]和f[i-1][v-c[i]]兩個子問題遞推出來,能否保證在推f[i][[v]時(即在第i次主循環中推f[v]時)能夠得到f[i-1][v]和

f[i-1][v-c[i]]的值呢?事實上,這要求在每次循環中我們以v=V......0的順序推 f[v],這樣才能保證推 f[v]時,f[v-c[i]]保存的是狀態 f[i-1][v-c[i]] 的值。

#include<iostream> #include<cstdio> #include<cstring> using namespace std; const int maxn = 105; int dp[maxn]; int c[maxn];//第i件物品耗費的空間是Ci int w[maxn];//得到的價值是Wi。 int main() {int v;//背包容量int n;//N件物品while(scanf("%d %d",&v,&n) != EOF){for(int i = 0;i < n; i++){scanf("%d",&c[i]);}for(int i = 0;i < n; i++){scanf("%d",&w[i]);}//動態規劃邊界問題memset(dp,0,sizeof(dp));for(int i = 0;i < n; i++){//枚舉物品for(int j = v;j >= c[i]; j--){//枚舉背包容量//當前背包容量可以放下這件物品dp[j] = max(dp[j],dp[j - c[i]] + w[i]);}}printf("%d\n",dp[v]);}return 0; }

1.4 初始化的細節問題

求最優解的背包問題中,有兩種問法:

1.求恰好裝滿背包時的最優解。

初始化時,f[0]=0,f[1...v]為-INF(INF=0x3f3f3f3f),這樣最終得到的f[N]是一種恰好裝滿背包的最優解

2.沒有要求必須把背包裝滿,而是只希望價格盡量大,初始化時應該將f[0..V]全部設 為0。

初始化的f數組事實上就是在沒有任何物品可以放入背包時的合 法狀態。

如果要求背包恰好裝滿,那么此時只有容量為0的背包,可能的價值為0,只能被nothing“恰好 裝滿”,其它容量的背包均沒有合法的解,屬于未定義的狀態,它們的值就都應該是?∞了。

如果 背包并非必須被裝滿,那么任何容量的背包都有一個合法解“什么都不裝”,這個解的價值為0, 所以初始時狀態的值也就全部為0了。

二 完全背包問題

2.1題目

有N種物品和一個容量為V的背包,每種物品都有無限件可用。第i種物品的費用是c[i], 價值是w[i]。求解將哪些物品裝入背包可使這些物品的費用總和不超過背包容量,且價值總和最 大。

2.2基本思路

這個問題非常類似于01背包問題,所不同的是每種物品有無限件。也就是從每種 物品的角度考慮,與它相關的策略已并非取或不取兩種,而是有取0件、取1件、取2件……等很 多種。如果仍然按照解01背包時的思路,令f[i][v]表示前i種物品恰放入一個容量為v的背包的最 大權值。仍然可以按照每種物品不同的策略寫出狀態轉移方程:

f[i][v] = max{f[i?1][v?k×c[i]] + k×w[i]}???? 0 <= k×c[i] <= v

這跟01背包問題一樣有O(VN)個狀態需要求解,但求解每個狀態的時間已經不是常數了,求解 狀態f[i][v]的時間是Θ( v / c[i]),總的復雜度可以認為是Θ(V ×∑ V / c[i]),是比較大的。

#include<cstdio> #include<iostream> using namespace std; const int maxn = 1005; int dp[maxn][maxn]; int c[maxn],w[maxn]; int main() {int n,v;while(scanf("%d %d",&v,&n) != EOF){for(int i = 0;i < maxn; i++){dp[i][0] = dp[0][i] = c[i] = w[i] = 0;}for(int i = 1;i <= n; i++){scanf("%d",&c[i]);}for(int i = 1;i <= n; i++){scanf("%d",&w[i]);}for(int i = 1;i <= n; i++){for(int j = 1;j <= v; j++){dp[i][j] = dp[i - 1][j];if(c[i] <= j){dp[i][j] = max(dp[i][j],dp[i][j - c[i]] + w[i]);}}}printf("%d\n",dp[n][v]);}return 0; }

一個簡單有效的優化

完全背包問題有一個很簡單有效的優化:若兩件物品i、 j滿 足c[i] <= c[j]且w[i] >= w[j],則將物品j去掉,不用考慮。這個優化的正確性顯然:任何情況下都可 將價值小費用高得j換成物美價廉的i,得到至少不會更差的方案。對于隨機生成的數據,這個方 法往往會大大減少物品的件數,從而加快速度。然而這個并不能改善最壞情況的復雜度,因為 有可能特別設計的數據可以一件物品也去不掉。

首先將費用大于V的物品去掉,然后使用類似計數排序的做法,計算出費用相同 的物品中價值最高的是哪個,可以Θ(V + N)地完成這個優化。

//完全背包 #include<bits/stdc++.h> using namespace std; const int maxn=1e4+10; int n;//n種物品 int v;//背包容量 int c[maxn]; int w[maxn]; int dp[maxn]; int main() {while(scanf("%d%d",&v,&n)!=EOF){for(int i=0;i<n;i++)scanf("%d",&c[i]);//每個物品的體積for(int i=0;i<n;i++)scanf("%d",&w[i]);//每物品的價值memset(dp,0,sizeof(dp));for(int i=0;i<n;i++)//枚舉所有的物品for(int j=c[i];j<=v;j++)//枚舉背包容量dp[j]=max(dp[j],dp[j-c[i]]+w[i]);//狀態轉移方程printf("%d\n",dp[v]);}return 0; }

三 多重背包問題

3.1 題目

有N種物品和一個容量為V 的背包。第i種物品最多有Mi件可用,每件耗費 的空間是Ci,價值是Wi。求解將哪些物品裝入背包可使這些物品的耗費的空間 總和不超過背包容量,且價值總和最大。

3.2 基本算法

這題目和完全背包問題很類似。基本的方程只需將完全背包問題的方程略 微一改即可。 因為對于第i種物品有Mi+1種策略:取0件,取1件……取Mi件。令F[i, v]表 示前i種物品恰放入一個容量為v的背包的最大價值,則有狀態轉移方程:

F[i,v] = max{ F[i ? 1, v ? k ? Ci ] + k ? Wi | 0 ≤ k ≤ Mi }? ? ? ?復雜度是O(V * ΣMi)。

3.3 轉化為01背包問題

另一種好想好寫的基本方法是轉化為01背包求解:

方法一:O(V * ΣMi)?

把第i種物品換成Mi件01 背包中的物品,則得到了物品數為ΣMi的01背包問題。直接求解之,復雜度仍 然是O(V * ΣMi)。

#include<iostream> #include<cstdio> using namespace std; const int maxn = 5005; int dp[maxn][maxn]; int c[maxn];//第i件物品耗費的空間是Ci int w[maxn];//得到的價值是Wi。 int main() {int v;//背包容量int n;//N件物品int T;scanf("%d",&T);while(T--){scanf("%d %d",&v,&n);int p=1;for(int i = 1;i <= n; i++){int cost,value,num;scanf("%d%d%d",&cost,&value,&num);while(num--){c[p]=cost;w[p]=value;p++;} }//動態規劃邊界問題for(int i = 0;i <= n; i++){dp[i][0] = dp[0][i] = 0;}for(int i = 1;i <= p; i++){//枚舉p件物品for(int j = 1;j <= v; j++){//枚舉背包容量if(j < c[i]){ //當前背包容量放不下這件物品dp[i][j] = dp[i - 1][j];}else{ //當前背包容量可以放下這件物品dp[i][j] = max(dp[i - 1][j],dp[i - 1][j - c[i]] + w[i]);}}}printf("%d\n",dp[p-1][v]);}return 0; }

方法二:O(V * ΣlogMi)?

但是我們期望將它轉化為01背包問題之后,能夠像完全背包一樣降低復雜 度。 仍然考慮二進制的思想,我們考慮把第i種物品換成若干件物品,使得原問 題中第i種物品可取的每種策略——取0 . . . Mi件——均能等價于取若干件代換 以后的物品。另外,取超過Mi件的策略必不能出現。

方法是:將第i種物品分成若干件01背包中的物品,其中每件物品有一個系 數。這件物品的費用和價值均是原來的費用和價值乘以這個系數。令這些系數 分別為1, 2, 2 ^2 . . . 2 ^ (k?1) , Mi ? 2^ (k ) + 1,且k是滿足Mi ? 2 ^ k + 1 > 0的最大整數。

例 如,如果Mi為13,則相應的k = 3,這種最多取13件的物品應被分成系數分別 為1, 2, 4, 6的四件物品。 分成的這幾件物品的系數和為Mi,表明不可能取多于Mi件的第i種物品。另 外這種方法也能保證對于0 . . . Mi間的每一個整數,均可以用若干個系數的和表 示。這里算法正確性的證明可以分0 . . . 2 ^ (k?1)和2 ^ k . . . Mi兩段來分別討論得出, 這樣就將第i種物品分成了O(logMi)種物品,將原問題轉化為了復雜度 為O(V * ΣlogMi)?的01背包問題,是很大的改進。

#include<iostream> #include<cstdio> using namespace std; const int maxn = 5005; int dp[maxn][maxn]; int c[maxn];//第i件物品耗費的空間是Ci int w[maxn];//得到的價值是Wi。 int main() {int v;//背包容量int n;//N件物品int T;scanf("%d",&T);while(T--){scanf("%d %d",&v,&n);int p=1;for(int i = 1;i <= n; i++){int cost,value,num;scanf("%d%d%d",&cost,&value,&num);int k=1;while(num-(k*2)+1>0){c[p]=k*cost;w[p]=k*value;k*=2;p++;}k=(num-k+1);c[p]=k*cost;w[p]=k*value;p++; }//動態規劃邊界問題for(int i = 0;i <= n; i++){dp[i][0] = dp[0][i] = 0;}for(int i = 1;i <= p; i++){//枚舉p件物品for(int j = 1;j <= v; j++){//枚舉背包容量if(j < c[i]){ //當前背包容量放不下這件物品dp[i][j] = dp[i - 1][j];}else{ //當前背包容量可以放下這件物品dp[i][j] = max(dp[i - 1][j],dp[i - 1][j - c[i]] + w[i]);}}}printf("%d\n",dp[p-1][v]);}return 0; }

?

總結

以上是生活随笔為你收集整理的01背包问题+完全背包问题+多重背包问题的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。