背包问题 c语言版
?b站背包問題專題講解
背包問題入門看《圖解算法》中9.1“背包問題”章節較好理解
背包問題目錄
- ==一、01背包問題==
- 輸入格式
- 輸出格式
- 數據范圍
- 輸入樣例
- 輸出樣例
- ==二、完全背包問題==
- ==三、多重背包問題 1==
- 輸入樣例
- 輸出樣例
- ==四、多重背包問題 2==
- 數據范圍
- ==五、多重背包問題 3 終極版== 男人八題之一
- 數據范圍
- ==六、混合背包問題==
- 輸入格式
- 輸出格式
- 數據范圍
- 輸入樣例
- 輸出樣例:
- ==七、二維費用背包問題==
- 輸入格式
- 輸出格式
- 數據范圍
- 輸入樣例
- 輸出樣例
- ==八、分組背包問題==
- 輸入格式
- 輸出格式
- 數據范圍
- 輸入樣例
- 輸出樣例:
一、01背包問題
有 N 件物品和一個容量是 V 的背包。每件物品只能使用一次。
第 i 件物品的體積是vi,價值是 wi。
求解將哪些物品裝入背包,可使這些物品的總體積不超過背包容量,且總價值最大。
輸出最大價值。
輸入格式
第一行兩個整數,N,V,用空格隔開,分別表示物品數量和背包容積。
接下來有 N 行,每行兩個整數 vi,wi,用空格隔開,分別表示第 i 件物品的體積和價值。
輸出格式
輸出一個整數,表示最大價值。
數據范圍
0 < N,V ≤ 1000
0 < vi,wi ≤ 1000
輸入樣例
4 5 1 2 2 4 3 4 4 5輸出樣例
8 #二維動態規劃 f[i][j]表示只看前i個物品,總體積是j的情況下,總價值最大是多少 result = max{f[n][0~V]} f[i][j]: 1.不選第i個物品,f[i][j] = f[i - 1][j] 2.選第i個物品,f[i][j] = f[i - 1][j - v[i]] f[i][j] = max{1,2,} 初始化:f[0][0] = 0; #include<stdio.h> int max(int a,int b){if(a>=b) return a;else return b; } int main(){int N=500;int n,m; //n是物品數,m是背包體積int f[N][N];int v[N],w[N];scanf("%d %d",&n,&m);for(int i=1;i<=n;i++) scanf("%d %d",&v[i],&w[i]);for(int i=1;i<=n;i++)for(int j=0;j<=m;j++){f[i][j] = f[i-1][j];if(j >= v[i])f[i][j] = max(f[i][j], w[i] + f[i-1][j-v[i]]);}printf("%d",f[n][m]);return 0; }我們發現每次再對新的一行進行計算時,只用到了上一行的信息,那么我們可以嘗試把二維背包變為一維,如下:
int f[N];int v[N],w[N];scanf("%d %d",&n,&m);for(int i=1;i<=n;i++) scanf("%d %d",&v[i],&w[i]);for(int i=1;i<=n;i++)for(int j=m;j>=v[i];j--) //我們每次運用到的上一行的信息都在前面的位置,為了防止覆蓋掉,所以要從大到小f[j] = max(f[j],f[j-v[i]] + w[i]);?1.不管背包是否裝滿的所有可能中價值最大的,此類問題的二維數組應初始化全部為0。
2.要求背包必須恰好裝滿, 這樣需要把f [ 0 ] [ 0 ~ m] 初始化為0, 其余初始化為負無窮。保證結果一定是從f [0][x]傳遞過來。(0體積的背包)
二、完全背包問題
現在每一個物體都有無限個啦!可以重復選,其余部分與上一題完全相同。
直接一維背包開搞! f[i] 表示總體積為i的情況下,最大價值是多少 result = f [m]之前的代碼是上面一行傳遞給下面一行,這次是同一行中前一列的最大價值和上面一行的同一列 傳遞給后一個
實現如下:
三、多重背包問題 1
多了一個條件,第 i 種物品最多有 si 件
輸入樣例
分別對應 vi,wi ,si
4 5 1 2 3 2 4 1 3 4 3 4 5 2輸出樣例
10 f[i] 總體積是i的情況下,最大價值是多少for(int i = 0; i < n; i++) {for(int j = m; j >= v[i]; j--)f[j] = max(f[j], f[j-v[i]] + w[i], f[j - 2 * v[i]] + 2 * w[i]...); //到s[i]個 做個循環 } 1. f[i] = 0; 所有初始化為0result = f[m] 2. f[0] = 0; 其余初始化為復無窮 恰好裝滿result = max{f[0...m]}可以由01背包修改而來,只不過要多一個循環對于每一件物體拿不同數量的比較
#include<stdio.h> int max(int a,int b){if(a>=b) return a;else return b; } int n,m; int f[110]; int main(){scanf("%d %d",&n,&m);for(int i=0 ; i<n ;i++){int v,w,s;scanf("%d%d%d",&v,&w,&s);for(int j=m ; j>=0 ; j--){for(int k=1 ; k<=s && k*v <= j ; k++){f[j] = max(f[j], f[j - k * v] + k * w);}}}printf("%d",f[m]); }四、多重背包問題 2
數據范圍
0 < N ≤ 1000
0 < V ≤ 2000
0 < vi,wi,si ≤2000
與上一道問題完全一樣,是多重背包的二進制優化方法 ,可以算數量級更大的輸入
可以把每種物體拆開為 s!個,從而轉換為01背包問題看, 但數量級還是很大,用二進制拆法
給定任意數x,最少選多少個數,可以拼成<=x 的所有的數呢 ?
答案是 log2(x) 的上取整
eg:
x = 7 選 1,2,4即可表示<=7的所有數字 7 = 1+2+4
x=10 選 1,2,4,3即可
這樣我們就不需要把同一種物體拆稱 si 個,而是拆成log2(si)上取整個物體,復雜度降低
#include<stdio.h> int max(int a,int b){if(a>=b) return a;else return b; } struct GOOD{ //創建物體結構體,用來創建拆出來的新物體 int v,w; }; int f[2010]; int n,m; int main(){struct GOOD goods[100000]; int t = 1; //放入goods結構體數組的計數器 scanf("%d%d",&n,&m);for(int i=0 ; i<n ; i++){int v,w,s;scanf("%d%d%d",&v,&w,&s);for(int k=1 ; k<=s ; k*=2){s -= k;goods[t].v = v*k;goods[t].w = w*k;t ++; }if(s>0) { //用來對應 10被分為 1,2,4,3的1情況 goods[t].v = v*s;goods[t].w = w*s;t++;}}for(int i=0 ; i<t ; i++) {for(int j=m ; j>=goods[i].v ; j--)f[j] = max(f[j], f[j - goods[i].v] + goods[i].w);}printf("%d",f[m]); }五、多重背包問題 3 終極版 男人八題之一
數據范圍
0 < N ≤ 1000
0 < V ≤ 20000
0 < vi,wi,si ≤ 20000
現在數量級超級大,用二進制優化方法也頂不住了
單調隊列優化方法 #我現在還不會等我學完了回來補充
大佬題解
六、混合背包問題
有 N 種物品和一個容量是 V 的背包。
物品一共有三類:
第一類物品只能用1次(01背包);
第二類物品可以用無限次(完全背包);
第三類物品最多只能用 si 次(多重背包);
每種體積是 vi,價值是 wi。
求解將哪些物品裝入背包,可使物品體積總和不超過背包容量,且價值總和最大。
輸出最大價值。
輸入格式
第一行兩個整數,N,V,用空格隔開,分別表示物品種數和背包容積。
接下來有 N 行,每行三個整數 vi, wi, si,用空格隔開,分別表示第 i 種物品的體積、價值和數量。
si = ?1 表示第 i 種物品只能用 1 次;
si = 0 表示第 i 種物品可以用無限次;
si > 0 表示第 i 種物品可以使用 si 次;
輸出格式
輸出一個整數,表示最大價值。
數據范圍
0 < N, V ≤ 1000
0 < vi, wi ≤ 1000
?1 ≤ si ≤ 1000
輸入樣例
4 5 1 2 -1 2 4 1 3 4 0 4 5 2輸出樣例:
8先判斷這個物體屬于哪一類,然后按那一類去轉移
遇到01背包或者完全背包就直接存下來,如果是多重背包就拆成log2個存入
#include<stdio.h> int max(int a,int b){if(a>=b) return a;else return b; } int n,m; int f[1010];struct GOOD{int kind;int v;int w; };struct GOOD goods[10010]; int t=1; int main(){scanf("%d%d",&n,&m);for(int i=0; i<n ;i++){ //錄入物體數組int v,w,s;scanf("%d%d%d",&v,&w,&s);if(s<0) {goods[t].kind=-1;goods[t].v=v;goods[t].w=w;t++;}else if(s==0){goods[t].kind=0;goods[t].v=v;goods[t].w=w; t++;}else{for(int k=1; k<=s ; k*=2){s -= k;goods[t].kind = -1; //拆完以后就變成01背包了 ,所以是-1 goods[t].v = v*k;goods[t].w = w*k;t++;}if(s>0){goods[t].kind = -1; goods[t].v = v*s;goods[t].w = w*s;t++;}}}//遍歷物體數組for(int i=1 ; i<t ; i++){ if(goods[i].kind < 0){ //01背包從大到小 for(int j=m ; j>=goods[i].v ; j--) {f[j] = max(f[j], f[j-goods[i].v] + goods[i].w);}}else{ //完全背包從小到大 for(int j=goods[i].v ; j<=m ; j++){f[j] = max(f[j], f[j-goods[i].v] + goods[i].w);} }}printf("%d",f[m]); }七、二維費用背包問題
有 N 件物品和一個容量是 V 的背包,背包能承受的最大重量是 M。
每件物品只能用一次。體積是 vi,重量是 mi,價值是 wi。
求解將哪些物品裝入背包,可使物品總體積不超過背包容量,總重量不超過背包可承受的最大重量,且價值總和最大。
輸出最大價值。
輸入格式
第一行三個整數,N , V , M,用空格隔開,分別表示物品件數、背包容積和背包可承受的最大重量。
接下來有 N 行,每行三個整數 vi, mi, wi,用空格隔開,分別表示第 i 件物品的體積、重量和價值。
輸出格式
輸出一個整數,表示最大價值。
數據范圍
0 < N ≤ 1000
0 < V, M ≤ 100
0 < vi, mi ≤100
0 < wi ≤ 1000
輸入樣例
4 5 6 1 2 3 2 4 4 3 4 5 4 5 6輸出樣例
8f[i][j] 為體積是i,重量是j時背包的最大價值。
就是把質量看成和體積同等地位的一種限制條件,再來次循環 ,若再多一個限制條件就變成三維數組即可
#include<stdio.h> int max(int a,int b){if(a>=b) return a;else return b; } int f[110][110]; int main(){int n,v,m;scanf("%d %d %d",&n,&v,&m);for(int i=0;i<n;i++){int a,b,c;scanf("%d%d%d",&a,&b,&c);for(int j=v;j>=a;j--)for(int k=m;k>=b;k--) //就是把質量看成和體積同等地位的一種限制條件,再來次循環 f[j][k]=max(f[j][k],f[j-a][k-b]+c);}printf("%d",f[v][m]); }八、分組背包問題
有 N 組物品和一個容量是 V 的背包。
每組物品有若干個,同一組內的物品最多只能選一個。
每件物品的體積是 vij,價值是 wij,其中 i 是組號,j 是組內編號。
求解將哪些物品裝入背包,可使物品總體積不超過背包容量,且總價值最大。
輸出最大價值。
輸入格式
第一行有兩個整數 N,V,用空格隔開,分別表示物品組數和背包容量。
接下來有 N 組數據:
每組數據第一行有一個整數 Si ,表示第 i 個物品組的物品數量;
每組數據接下來有 Si 行,每行有兩個整數 vij , wij ,用空格隔開,分別表示第 i 個物品組的第 j 個物品的體積和價值;
輸出格式
輸出一個整數,表示最大價值。
數據范圍
0 < N, V ≤ 100
0 < Si ≤ 100
0 < vij, wij ≤ 100
輸入樣例
3 5 2 1 2 2 4 1 3 4 1 4 5輸出樣例:
8 f[i][j] 為前i組物品,體積是j的情況下,背包的最大價值 //很像01背包問題,只是其中每個物品都可以在此細分并且要選出其中最優的那個 for(int i = 0; i<n ; i++){ //循環每一組for(int j=m;j>=v;j--){ //循環體積for() //循環組中每一個物品 , 也可以一個不選f[j] = max(...);} }多重背包問題是分組背包問題的特殊情況,多重背包問題是每一個物體有s+1種選法(不選,和選1到s個),可以想象把這s+1種選法變成s個物體打包成一個物體組變為分組背包問題,分組背包問題也是s+1種,但沒法優化。
總結
- 上一篇: 可靠消息最终一致性设计_如何最终启动您的
- 下一篇: spring mvc学习(45):spr