您的位置:首页 > 其它

动态规划: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个单位的物品,如何让背包里装入的物品具有最大的价值总和?

下表是按照上述算法的计算结果,表格计算顺序为自下向上,从左到右。

iweightvalue012345678910
1260066991212151515
2230033669991011
3650000666661011
4540000666661010
54600006666666
例如,对于m(2,6)代表的是背包容量为6,可选择物品为2,3,4,5时0-1背包的最优值。那么m(2,6)=max{m(3,6),m(3,6−2)+3}=max{6,9}=9

代码

有一点需要注意的是:在上面描述问题的时候始终是从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做一个选择
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: