1.问题描述
假设有n件物品,分别编号为1, 2...n。其中编号为i的物品价值为vi,它的重量为wi。为了简化问题,假定价值和重量都是整数值。现在,假设有一个背包,它能够承载的重量是W。如何往包里装这些物品,使得包里装的物品价值最大化。
用数学语言描述问题:
2.问题思路分析
我们知道分治法的核心是可以把问题不断分成更小的、更好解决的子问题,然后解决子问题,子问题再合成原问题。但是分治法如果分的子问题如果不独立的话,会在重复的求解共同的子问题。这个问题需要一种另一种思路-----动态规划。
可以假设现在求解出了最优解,是1...k(k<n)个物体,对第i个物体,想象一下,如果说拿掉这个物体,剩下的部分针对W-w[i]是不是最优解?用反证法来证明一下,假设不是最优解,那么肯定存在更优的解,那这个更优的解加上我们拿掉的i物体,能够组成比我们求解出的最优解更优,与我们的假设是相违背的。因此,拿掉i物体,剩下的物体依然是W-w[i]的最优解。
在n个物体中,假设c[i][j](表示第i个物体,背包剩j的容量)是前i个物体的最优解,那么对第i+1个物体,只有两种情况;
情况1:拿,那么当前的价值为c[i][j]+v[i+1],达到第i+1的最优解;
情况2:不拿,最优解仍然为c[i][j],达到第i+1的最优解;
背包为空或者物体重量超过背包容量时,效益为0;
公式最终可归纳为:
注:对于初学者来说,一开始可能不好理解,建议看一下youtube上的对0-1背包解释的一个视频Dynamic Programming:0/1 Knapsack Problem
3.实现
/*******************************************
0-1背包问题
作者:Andrewseu
日期:2015/12/11
*******************************************/
#include<iostream>
#include<iomanip>
using namespace std;
int value[100];//benifit
int weight[100];//weight
int c[100][100];
void knapsack(int n,int w){
for (int i = 1; i <= n; ++i){
for (int j = 1; j <= w; ++j){
//若第i个物体加进来不超过背包总重量并且价值比之前大,则改变最优值,否则保持不变
if (weight[i] <= j && c[i - 1][j-weight[i]] + value[i] > c[i - 1][j])
c[i][j] = c[i - 1][j-weight[i]] + value[i];
else
c[i][j] = c[i-1][j];
}
}
}
int main(){
int n, w;
cout << "Enter the number and weight:";
cin >> n >> w;
for (int i = 1; i <= n; ++i){
cin >> weight[i]>>value[i];
}
knapsack(n,w);
for (int i = 1; i <= n; ++i){
for (int j = 1; j <= w; ++j){
cout << setw(6)<<c[i][j] << " ";
}
cout << endl;
}
return 0;
}
测试输入:
n=4,W=5,weight{2,3,4,5},benefit{3,7,2,9}
输出:10