动态规划:01背包
2017-10-05 14:24
127 查看
问题描述
01背包是一个可以用动态规划解决的经典问题:给定n种物品和一背包。物品i的重量是wi ,其价值为vi,背包的容量为c。问应如何选择装入背包中的物品,使得装入背包的物品的总价值最大?在选择装入背包的物品时,对每种物品i只有两种选择,即装入背包或不装入背包。不能将物品i装入背包多次,也不能只装入部分的物品i。因此,称为01背包问题。
形式化描述
给定c>0,wi>0,vi>0,1≤i≤n,要求找出一个n元0-1向量(x1,x2,⋯,xn),xi∈{0,1},1≤i≤n, 使得∑i=1nwixi≤c, 而且∑i=1nvixi达到最大。因此,0-1背包是一个特殊的整数规划问题:max∑i=1nvixi
⎧⎩⎨∑i=1nwixi≤cxi∈{0,1},1≤i≤n
递归关系
01背包具有最优子结构性质,证明略。下面来根据其递归关系得到递推公式。设所给01背包问题的子问题
max∑k=invkxk
⎧⎩⎨∑k=inwkxk≤jxk∈{0,1},i≤k≤n
的最优值为m(i,j),即m(i,j)是背包容量为j,可选择物品为i,i+1,⋯,n时0-1背包问题的最优值。由0-1背包问题的最优子结构性质,分别考虑拿上或者不拿物品i, 以及背包剩余容量是否足够装下物品i,可以建立计算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(n,j)={vn j≥wn0 0≤j<wn
这样编写的程序会计算得到一张m(i,j)的二维表,最后的结果是(代码中下标从0开始):
ans=m(0,c)
计算过程
现在有五件物品,重量分别是2,2,6,5,4,价值分别是6,3,5,4,6,背包最多拿上重量10个单位的物品,如何让背包里装入的物品具有最大的价值总和?下表是按照上述算法的计算结果,表格计算顺序为自下向上,从左到右。
i | weight | value | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1 | 2 | 6 | 0 | 0 | 6 | 6 | 9 | 9 | 12 | 12 | 15 | 15 | 15 |
2 | 2 | 3 | 0 | 0 | 3 | 3 | 6 | 6 | 9 | 9 | 9 | 10 | 11 |
3 | 6 | 5 | 0 | 0 | 0 | 0 | 6 | 6 | 6 | 6 | 6 | 10 | 11 |
4 | 5 | 4 | 0 | 0 | 0 | 0 | 6 | 6 | 6 | 6 | 6 | 10 | 10 |
5 | 4 | 6 | 0 | 0 | 0 | 0 | 6 | 6 | 6 | 6 | 6 | 6 | 6 |
代码
有一点需要注意的是:在上面描述问题的时候始终是从1开始计数的,代码的话是从下标0开始的。可以在网上找一些数据测试一下程序。#include <iostream> using namespace std; //设定一个最大的背包容量+1的值 #define MAXCP1 10000 void Knapsack(int v[], int w[], int c, int n, int m[][MAXCP1]); int main() { int c, n; //背包容量c 物品个数n cin >> c >> n; int w , v , m [MAXCP1]; //n个物品的重量w ,n个物品的价值v ,以及二维数组m for (int i = 0; i < n; i++) { cin >> w[i] >> v[i]; } Knapsack(v, w, c, n, m); cout << m[0][c] << endl; return 0; } void Knapsack(int v[], int w[], int c, int n, int m[][MAXCP1]) { /* *初始化 */ for (int j = 0; j < w[n-1]; j++) m[n-1][j] = 0; for (int j = w[n-1]; j <= c; j++) m[n-1][j] = v[n-1]; /* *根据递推关系得到一张二维表 */ for (int i = n - 2; i > 0; i--) { for (int j = 0; j < w[i]; j++) m[i][j] = m[i+1][j]; for (int j = w[i]; j <= c; j++) m[i][j] = max(m[i+1][j], m[i+1][j-w[i]] + v[i]); } /* *得到最优值m[0][c] */ m[0][c] = m[1][c]; if (c >= w[0]) m[0][c] = max(m[1][c], m[1][c-w[0]] + v[0]); }
事实上,代码可以看起来更简洁,如下:
#include <iostream> #include <vector> using namespace std; void Knapsack(int v[], int w[], int c, int n, vector<int> &m); int main() { int c, n; //背包容量c 物品个数n cin >> c >> n; int w , v ; //n个物品的重量w ,n个物品的价值v for (int i = 0; i < n; i++) { cin >> w[i] >> v[i]; } vector<int> m(c+1, 0); //m[j]表示背包容量为j时可以拿到的最大价值 Knapsack(v, w, c, n, m); cout << m[c] << endl; return 0; } void Knapsack(int v[], int w[], int c, int n, vector<int> &m) { for (int i = 0; i < n; i++) { for (int j = c; j >= w[i]; j--) m[j] = max(m[j], m[j - w[i]] + v[i]); //表示从拿或者不拿物品i做一个选择 } }
相关文章推荐
- 最大报销额(动态规划:01背包问题变形)
- 动态规划之01背包
- 动态规划求解01背包相关的基本问题
- 动态规划之01 背包---Hd2602 Bone Collector
- 动态规划之01背包、完全背包问题
- 01背包问题的动态规划解法
- 动态规划分析—以01背包为例
- 动态规划之01背包
- 01背包问题 -- 经典动态规划题
- 【初学动态规划】之01背包问题
- 动态规划:01背包、完全背包
- 01背包问题之动态规划
- 动态规划的两个经典问题--01背包
- 算法细节系列(9):动态规划之01背包
- 动态规划之背包问题01——Java实现
- 动态规划之背包问题01——Java实现
- 动态规划之01背包,完全背包,分组背包
- 01背包问题动态规划详解
- 动态规划之01背包—小P寻宝记——粗心的基友
- 动态规划01背包问题