0-1背包问题 自底向上非递归的动态规划(C++)
2020-06-01 05:12
811 查看
文章目录
一、关于0-1背包问题
1. 问题描述:
- 给定 n 种物品和一个容量为 C 的背包,物品 i 的重量是 wi>0,其价值为 vi>0 。
- 问:应该如何选择装入背包的物品,使得装入背包中的物品的总价值最大?
2. 问题分析:
- ······
- 有问题的最优子结构,我们可以建立m(i, j)的递归表达式如下:
m(i,j)={max{m(i+1,j),m(i+1,j−wi)+vi}j≥wim(i+1,j)0≤j≤wi m(i,j)=\begin{cases} max\{m(i+1,j),m(i+1,j-w_i)+v_i\} & j \geq w_i \\ m(i+1,j) & 0 \leq j \leq w_i \end{cases} m(i,j)={max{m(i+1,j),m(i+1,j−wi)+vi}m(i+1,j)j≥wi0≤j≤wi
m(n,j)={vnj≥wi00≤j≤wi ⋅ m(n,j)=\begin{cases} v_n & j \geq w_i \\ 0 & 0 \leq j \leq w_i \end{cases} · m(n,j)={vn0j≥wi0≤j≤wi ⋅ - 例:假设现在有5件物品,分别重1, 2, 3, 4, 5,分别价值2, 3, 4, 5, 6,背包容量12。
j1 j2 j3 j4 j5 j6 j7 j8 j9 j10 j11 j12 i1 0 0 0 0 0 0 0 0 0 0 0 16 i2 0 3 4 5 7 8 9 10 12 13 14 15 i3 0 0 4 5 6 6 9 10 11 11 11 15 i4 0 0 0 5 6 6 6 6 11 11 11 11 i5 0 0 0 0 6 6 6 6 6 6 6 6 - 单元格 (i5, j3) 的意义是:子问题“只有物品 i5 时,容量为 j3 的背包所能放入的最大价值为0”,因为容量3的背包放不下重量5的物品;
- 单元格 (i4, j3) 的意义是:子问题“只有物品 i4 , i5 时,容量为 j3 的背包所能放入的最大价值为0”,因为容量3的背包放不下这两个物品;
- 其余单元格同理。。。
- 至此,全表表示了全部子问题的解,单元格 (i1, j12) 即为总问题的解。
二、算法实现
1. 自底向上 非递归 的动态规划
// 0-1背包问题的自底向上非递归的动态规划 #include<iostream> #include<cstdio> #include<algorithm> using namespace std; int n; // 物品个数 int c; // 背包容量 int* w; // 记录每个物品的重量 int* v; // 记录每个物品的价值 int* x; // 记录物品是否被放入背包 int** m; // 记录最大价值 void Data_input(); void MaxValue(); void OptimalSolution(); int main() { cout << "0-1背包问题 自底向上非递归的动态规划\n\n"; Data_input(); // 用户输入 MaxValue(); // 最大价值表 OptimalSolution(); // 最优解 cout << "\n可选的最大价值是:" << m[1][c] << endl; cout << "所选的物品情况是:"; for (int i = 1;i <= n;i++) cout << x[i] << " "; cout << endl; } // 用户输入 void Data_input() { cout << "请输入物品个数:"; cin >> n; cout << "请输入背包容量:"; cin >> c; w = new int[n + 1]; v = new int[n + 1]; x = new int[n + 1]; m = new int* [n + 1]; for (int i = 0;i <= n;i++) { m[i] = new int[c + 1]; memset(m[i], 0, sizeof(int) * (c + 1)); // 将表m所有元素设为0 } cout << "请输入物品的重量和价值:\n"; for (int i = 1;i <= n;i++) { cout << "#" << i << ":"; cin >> w[i]; cin >> v[i]; } } // 最大价值表 void MaxValue() { int jMax = min(w[n] - 1, c); // 避免存在单个重量超出背包容量的物品使数组m越界 // 以下两个for循环是最大价值表m的最后一行 for (int j = 0;j <= jMax;j++) m[n][j] = 0; for (int j = w[n];j <= c;j++) m[n][j] = v[n]; // 以下是表m最后一行以上的部分 for (int i = n - 1;i > 1;i--) { jMax = min(w[i] - 1, c); for (int j = 0;j <= jMax;j++) // 背包容量<物品重量,即装不下;将m[i+1][j](下一行同列)的值赋给m[i][j] m[i][j] = m[i + 1][j]; for (int j = w[i];j <= c;j++) // 背包容量>=物品重量,即可以装下; // 放:m[i][j]=m[i + 1][j - w[i]] + v[i] // 不放:m[i][j]=m[i + 1][j] // 哪个价值大,就采取哪种方式 m[i][j] = max(m[i + 1][j], m[i + 1][j - w[i]] + v[i]); } m[1][c] = m[2][c]; if (c >= w[1]) m[1][c] = max(m[1][c], m[2][c - w[1]] + v[1]); } // 最优解 void OptimalSolution() { int ctemp = c; // 临时背包容量 for (int i = 1;i < n;i++) { if (m[i][ctemp] == m[i + 1][ctemp]) x[i] = 0; // 价值相等,说明没有放入这个物品,0 else { x[i] = 1; // 价值不相等,说明放入了这个物品,1 ctemp = ctemp - w[i]; // 放入了一个物品,背包剩余容量为原容量-物品重量 } } x[n] = (m[n][c]) ? 1 : 0; // 最后一个物品,非0为true放入,0为false不放人 }
2. 运行结果展示
·
*其它一些常见算法请参阅此链接~
最后,非常欢迎大家来讨论指正哦!
相关文章推荐
- C++动态规划之背包问题之多重背包求方案数之 : 新年去世(趣事)之打牌 运用递归+数组输出多重背包的路径(用了哪些物品)
- 0-1背包问题动态规划代码实现(C++实现)
- 完全背包问题动态规划c++
- 动态规划 | 背包问题 | Knapsack Problem | C/C++实现
- 动态规划 背包问题 C++
- 背包问题---递归及动态规划
- 动态规划6-背包问题+记忆递归
- 动态规划解背包问题/C++/Knapsack problem
- 0-1背包问题与动态规划的C/C++代码
- 背包问题---递归及动态规划
- 动态规划解背包问题/C++/Knapsack problem
- 动态规划:0-1背包问题(使用递归方法)
- 动态规划解背包问题/C++/Knapsack problem
- 背包问题与动态规划问题初学
- 动态规划问题一:背包问题
- 动态规划-0-1背包问题
- 动态规划:0-1背包问题(使用迭代方法,避免重复计算)
- 动态规划---0/1背包问题
- 动态规划之背包问题初研究
- 动态规划:完全背包问题-HDU1114-Piggy-Bank