首页 > 代码库 > 动态规划——0-1背包问题
动态规划——0-1背包问题
0-1背包问题:
描述:给定n中物品和一背包。物品i的重量是wi,其价值为vi,背包的容量为c,问应如何选择装入背包中的物品,使得装入背包中的物品总价值最大?
0-1背包问题是一个特殊的整数规划问题。
设所给0-1背包问题的子问题;
其最优值为m(i,j),即m(i,j)是背包容量为j,可选择物品为i,i+1,…,n时0-1背包问题的最优值。由0-1背包问题的最优子结构性质,可以建立计算m(i,j)的递归式如下:
NO1:递归实现
1 /* 2 *Description 递归实现 3 *设有一个背包可以放入的物品重量为S,现有n件物品,重量分别是w1,w2,w3,…wn。 4 *问能否从这n件物品中选择若干件放入背包中,使得放入的重量之和正好为S。 5 *如果有满足条件的选择,则此背包有解,否则此背包问题无解。 6 */ 7 # include<stdio.h> 8 # include<string.h> 9 10 int date[1005]; 11 12 int f(int w,int s) 13 { 14 if(w==0) return 1;//正好 15 if(w<0||w>0 &&s==0) return 0; 16 if(f(w-date[s],s-1)) return 1;//退出来再选下一个 17 return f(w,s-1);//选择下一个 18 } 19 20 int main() 21 { 22 int i,Weight,n; 23 while(scanf("%d %d",&Weight,&n)!=EOF) 24 { 25 memset(date,0,sizeof(date)); 26 for(i=1;i<=n;i++) 27 scanf("%d",&date[i]); 28 if(f(Weight,n)) 29 printf("YES\n"); 30 else 31 printf("NO\n"); 32 } 33 return 0; 34 }
NO2:动态规划——从后往前实现
1 /* 2 *从后往前得到最终结果 3 *二维数组m[][]来存储m(i,j)的相应值 4 *i:物品的取值范围(i,n) 5 *j:当前背包的最大容量值 6 */ 7 8 #include <stdio.h> 9 #include <stdlib.h> 10 #include <math.h> 11 #include <windows.h> 12 13 # define max(a,b) a>b?a:b 14 # define min(a,b) a<b?a:b 15 16 void Knapsack(int *v, int *w,int c, int n, int m[][100]) 17 { 18 int i,j,jmax; 19 jmax=min(w[n]-1, c); //装不下n号物品的最大重量 20 for(j = 0; j <= jmax; j++) 21 m[n][j] = 0; 22 for(j = w[n]; j <= c; j++) 23 m[n][j] = v[n]; 24 for(i = n-1; i > 1; i--) 25 { 26 jmax = min(w[i]-1, c); 27 for(j = 0; j <= jmax; j++) 28 m[i][j] = m[i+1][j]; 29 for(j = w[i]; j <= c; j++) 30 m[i][j] = max(m[i+1][j],m[i+1][j - w[i]] + v[i]); 31 } 32 m[1][c] = m[2][c]; 33 if(c >= w[1]) 34 m[1][c] = max(m[2][c],m[2][c - w[1]] + v[1]); 35 } 36 37 void Traceback(int m[][100], int *w, int c, int n, int *x) 38 { 39 int i; 40 for(i = 1; i < n; i++) 41 if(m[i][c] == m[i + 1][c]) 42 x[i] = 0; 43 else 44 { 45 x[i] = 1; 46 c -= w[i]; //背包容量的变化,逐渐减少 47 } 48 x[n] = (m[n][c]) ? 1 : 0; 49 } 50 51 void main() 52 { 53 int j; 54 int i,n,c; 55 int v[100]={0},w[100]={0}; 56 int m[100][100] = {0}; 57 int x[100]={0}; 58 59 printf("please input the num : n = "); 60 scanf("%d",&n); 61 printf("\nplease input the capacitance : c = "); 62 scanf("%d",&c); 63 printf("\nplease input the value :\n"); 64 for(i = 1; i <= n; i++) 65 scanf("%d",&v[i]); 66 printf("\nplease input the weight :\n"); 67 for(i = 1; i <= n; i++) 68 scanf("%d",&w[i]); 69 70 Knapsack(v,w,c,n,m); 71 72 Traceback(m,w,c,n,x); 73 74 printf("\nthe result is :"); 75 for(i = 1; i <= n; i++) 76 if(x[i]==1) 77 printf("%d ",i); 78 Sleep(10000000); 79 80 }
NO3:动态规划——从前往后实现
1 /* 2 *从前往后得到最终结果 3 *二维数组m[][]来存储m(i,j)的相应值 4 *i:物品的取值范围(1,i) 5 *j:当前背包的最大容量值 6 */ 7 8 #include <stdio.h> 9 #include <stdlib.h> 10 #include <math.h> 11 #include <windows.h> 12 13 # define max(a,b) a>b?a:b 14 # define min(a,b) a<b?a:b 15 16 void Knapsack(int *v, int *w,int c, int n, int m[][100]) 17 { 18 int i,j,jmax; 19 jmax=min(w[n]-1, c); 20 for(j = 0; j <= jmax; j++) 21 m[n][j] = 0; 22 for(j = w[n]; j <= c; j++) 23 m[n][j] = v[n]; 24 for(i = n-1; i > 1; i--) 25 { 26 jmax = min(w[i]-1, c); 27 for(j = 0; j <= jmax; j++) 28 m[i][j] = m[i+1][j]; 29 for(j = w[i]; j <= c; j++) 30 m[i][j] = max(m[i+1][j],m[i+1][j - w[i]] + v[i]); 31 } 32 m[1][c] = m[2][c]; 33 if(c >= w[1]) 34 m[1][c] = max(m[2][c],m[2][c - w[1]] + v[1]); 35 } 36 37 void Traceback(int m[][100], int *w, int c, int n, int *x) 38 { 39 int i; 40 for(i = 1; i < n; i++) 41 if(m[i][c] == m[i + 1][c]) 42 x[i] = 0; 43 else 44 { 45 x[i] = 1; 46 c -= w[i]; 47 } 48 x[n] = (m[n][c]) ? 1 : 0; 49 } 50 51 void Knapsack_pre(int *v, int *w,int c, int n, int m[][100]) 52 { 53 int i,j,jmax; 54 jmax=min(w[1]-1, c); 55 for(j = 0; j <= jmax; j++) 56 m[1][j] = 0; 57 for(j = w[1]; j <= c; j++) 58 m[1][j] = v[1]; 59 for(i = 2; i <= n; i++) 60 { 61 jmax = min(w[i]-1, c); 62 for(j = 0; j <= jmax; j++) 63 m[i][j] = m[i-1][j]; 64 for(j = w[i]; j <= c; j++) 65 m[i][j] = max(m[i-1][j],m[i-1][j - w[i]] + v[i]); 66 } 67 } 68 69 void Traceback_pre(int m[][100], int *w, int c, int n, int *x) 70 { 71 int i; 72 for(i = n; i > 0; i--) 73 if(m[i][c] == m[i - 1][c]) 74 x[i] = 0; 75 else 76 { 77 x[i] = 1; 78 c -= w[i]; 79 } 80 x[1] = (m[1][c]) ? 1 : 0; 81 } 82 83 void main() 84 { 85 int j; 86 int i,n,c; 87 int v[100]={0},w[100]={0}; 88 int m[100][100] = {0}; 89 int x[100]={0}; 90 91 printf("please input the num : n = "); 92 scanf("%d",&n); 93 printf("\nplease input the capacitance : c = "); 94 scanf("%d",&c); 95 printf("\nplease input the value :\n"); 96 for(i = 1; i <= n; i++) 97 scanf("%d",&v[i]); 98 printf("\nplease input the weight :\n"); 99 for(i = 1; i <= n; i++) 100 scanf("%d",&w[i]); 101 102 Knapsack_pre(v,w,c,n,m); 103 104 Traceback_pre(m,w,c,n,x); 105 106 printf("\n\n"); 107 printf("m :\n"); 108 for(i = 1; i <= n; i++) 109 { 110 for(j = 0; j <= c; j++) 111 printf("%d ",m[i][j]); 112 printf("\n"); 113 } 114 115 printf("\n\n"); 116 printf("x : \n"); 117 for(i = 1; i <= n; i++) 118 printf("%d ",x[i]); 119 120 printf("\nthe result is :"); 121 for(i = 1; i <= n; i++) 122 if(x[i]==1) 123 printf("%d ",i); 124 Sleep(10000000); 125 126 }
NO4:自顶向下的递归实现 + 自底向上实现 + 基于备忘录的自顶向下实现
1 #include <stdio.h> 2 3 #define N 4 4 #define W 5 5 6 //物品的重量 7 int w[] = {-1, 2, 1, 3, 2}; 8 9 //价值数组 10 int vi[] = {-1, 12, 10, 20, 15}; 11 12 int v[N+1][W+1]; //v[i][j]表示从前i个物品选能够放进承重量为j的背包的子集的最大总价值 13 14 void init() 15 { 16 int i, j; 17 for (i = 0; i <= N; i++) 18 for (j = 0; j <= W; j++) 19 v[i][j] = -1; 20 21 for (i = 0; i <= N; i++) 22 v[i][0] = 0; 23 24 for (i=0; i <= W; i++) 25 v[0][i] = 0; 26 } 27 28 29 //基于备忘录的动态规划算法 30 int MKFnapsack_MEMOIZE(int i, int j) 31 { 32 int value; 33 if (v[i][j] < 0) //如果v[i][j]还没有计算,则进行计算 34 { 35 if (j < w[i]) 36 value = http://www.mamicode.com/MKFnapsack_MEMOIZE(i-1,j); 37 else 38 { 39 int v1 = MKFnapsack_MEMOIZE(i-1, j); 40 int v2 = MKFnapsack_MEMOIZE(i-1, j-w[i]) + vi[i]; 41 value = http://www.mamicode.com/v1 >=v2 ? v1:v2; 42 } 43 v[i][j] = value; 44 } 45 return v[i][j]; //如果v[i][j]已经进行计算,则不进行计算,直接返回即可 46 } 47 48 //自顶向下的动态规划算法 49 int MKFnapsack_TOP_TO_BOTTOM(int i, int j) 50 { 51 int value; 52 53 if(i <= 0 || j <= 0) 54 return 0; 55 56 //不管v[i][j]是否计算过,都进行计算 57 if (j < w[i]) 58 value = http://www.mamicode.com/MKFnapsack_TOP_TO_BOTTOM(i-1, j); 59 else 60 { 61 int v1 = MKFnapsack_TOP_TO_BOTTOM(i-1, j); 62 int v2 = MKFnapsack_TOP_TO_BOTTOM(i-1, j-w[i]) + vi[i]; 63 value = http://www.mamicode.com/v1 >= v2 ? v1:v2; 64 } 65 66 return value; 67 } 68 69 //自底向上的算法 70 int MKFnapsack_BOTTOM_TO_TOP(int Ni, int Wi) 71 { 72 int i, j; 73 for (i = 1; i <= Ni; i++) 74 { 75 for(j = 1; j <= Wi; j++) 76 { 77 if(j < w[i]) 78 v[i][j] = v[i-1][j]; 79 else //j >=w[i] 80 { 81 int v1= v[i-1][j]; 82 int v2 = v[i-1][j-w[i]] + vi[i]; 83 v[i][j] = v1 >= v2 ? v1:v2; 84 } 85 } 86 } 87 return v[N][W]; 88 } 89 90 void print_v(int Ni, int Wi) 91 { 92 int i, j; 93 for(i = 0; i <= Ni; i++) 94 { 95 for(j = 0; j <= Wi; j++) 96 printf("%d ", v[i][j]); 97 printf("\n"); 98 } 99 } 100 101 int main() 102 { 103 printf("top to bottom most value is:%d\n", MKFnapsack_TOP_TO_BOTTOM(N, W)); 104 105 init();//数组初始化 106 printf("memoize most value is:%d\n", MKFnapsack_MEMOIZE(N, W)); 107 print_v(N, W); 108 109 init(); 110 printf("bottom to top most value is:%d\n", MKFnapsack_BOTTOM_TO_TOP(N, W)); 111 print_v(N, W); 112 113 return 0; 114 }
分析:
自顶向下的递归算法,写法最简单,但效率是最低的,它往往把问题搞成指数级。而自底向上的算法是DP的经典策略,它比自顶向下的效率高,但是,它往往也计算了没有必要计算的子问题(见上图)。而基于备忘录的自顶向下的算法是前两者的集大成者,效率最优。
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。