1. 问题描述
现在摆在你面前n件物品,已有一个旅行包,一共可以放c kg的东西。
N件物品时这样的:
重量依次是:
W1, W2, W2…….. Wn
价值:
V1, V2, V2…….. Vn
现在让你去选择这些物品,要让它价值最大,但是不能超过包的总重量。
2. 问题的数学定义
摆在面前的n件物品,你是选还是不选,可以用xi (0表示不选这件物品,1表示要选这件物品)来表示:
X1, X2, X2…….. Xn
那么纯数学定义就变成了,在
W1* X1+ W2* X2+…….. Wn* Xn <= c kg 的条件下,
求:V1* X1+ V2* X2+…….. Vn* Xn的最大值?
3. 0-1背包动态规划可行性
假设现在,你选了y1,y2,……,yn , 这是一个最优的解,那么同样的道理,y2,……,yn,同样是一个最优的解,这就是动态规划的精髓,如果一个问题,大问题的最优解,可以划分为小问题的最优解,那么,从小问题逐步逼向大问题,就可以求出结果。
4. 动态规划方程
自己现在才慢慢感觉,算法不是要你急于马上就把代码完美的写出来,一点一点分析问题,把模型建立起来才是最重要的,下面就来看看如何建立01背包的动态规划方程。
①先来定义一般情况下的子问题:
大问题是求1至n,现在子问题的规模是 : i,i+1,………n
将其转换为数学定义:
条件是:Wi* Xi+ Wi+1* Xi+1+…….. Wn* Xn <= j kg
目标是求:Vi* Xi+ Vi+1* Xi+1+…….. Vn* Xn 的最大值
m(i,j)表示:背包容量为j,可选择物品为i,i+1,…,n时0_1背包问题的最优值。 (1≤j≤c)
②对于一般情况的动态规划方程
一、边界条件(先我们从第n件物品开始取,当然也可以从第1件开始取)
Vn (如果第n个我选了, j>= Wn)
m(n,j)=
0 (如果第n个我选不了,j< Wn)
二、一般条件
Max{m(i+1,j),m(i+1,j- Wi) + Vi } (j- Wi>=0,容量够得时候第i个物品可以放或者选择不放)
m(I,j)=
m(i+1,j) (j- Wi<=0,容量不够,那就不放)
注:有的人可能会觉得Max{m(i+1,j),m(i+1,j- Wi) + Vi },肯定是m(i+1,j- Wi) + Vi比较大,因为加了一个Vi嘛。其实这里不然,要注意m(i+1,j)与m(i+1,j- Wi)是不相等的。
另外,我们这里是从第n件物品开始选的,所以选i时,(i+1,i+1,……..n)这个子问题是有解得,而且m(i+1,j),左边的参数是+1,如果我们从第1个开始选就会是 ‘-’ 了。
5. 举例说明
举个例子来看一下,程序应该如何解决下面这个实际的问题。
n=3, c=6
(v1, v2, v3) = (1, 2, 5),
(w1,w2,w3) = (2, 3, 4)
主要是看二维表怎么存数据的:(行表示物品,列表示重量为多少的包)
0 | 1 | 2 | 3 | 4 | 5 | 6 | |
1 | |||||||
2 | |||||||
3 | 0 | 0 | 0 | 0 | 5 | 5 | 5 |
0 | 1 | 2 | 3 | 4 | 5 | 6 | |
1 | |||||||
2 | 0 | 0 | 0 | 2 | 5 | 5 | 5 |
3 | 0 | 0 | 0 | 0 | 5 | 5 | 5 |
0 | 1 | 2 | 3 | 4 | 5 | 6 | |
1 | 0 | 0 | 1 | 2 | 5 | 5 | 6 |
2 | 0 | 0 | 0 | 2 | 5 | 5 | 5 |
3 | 0 | 0 | 0 | 0 | 5 | 5 | 5 |
请对照公式走一遍。
6. 构造最优解
由于m(1,6) ≠ m(2,6)Þ放物品1,x1=1, c=c-w1 = 4
比较m(2,4)与m(3,4)是否相等Þ不放物品2, x2=0
由于m(3,4) ≠0 Þ放物品3, x3 = 1
即:最优解为(1,0,1)
7. 代码
/************************************************************/
/*
题目:矩阵连乘问题
学号:2007110307
姓名:郭峰
完成时间:10月28日
*/
/************************************************************/
#include <stdio.h>
int max(int a,int b)
{
if(a > b)
return a;
else
return b;
}
void ZeroOneBag(int *v,int *w,int *x,int c,int n, int m[4][7])
{
int i,j;
for(j = 0; j < c; j++)
{
if (j < w[n]) //从第N件物品开始选,如果放不下
m[n][j]=0;
else //如果放的下
m[n][j]=v[n];
}
for(i = n-1; i >= 1; i--)//控制物品的循环,从i-1到第1件
{
for(j = 0; j < w[i]; j++)//当前这个物品放不下,则最优解为之前的解
m[i][j]=m[i+1][j];
for(j=w[i]; j<=c; j++) //当前这个物品能放进去,但是我可以放进去,也可以不放进去,另外如果我放进去了,
//那么将对之前的最优解有影响
m[i][j]=max(m[i+1][j], m[i+1][j-w[i]]+v[i]);
}
for(i = 1; i < n; i ++)//构造最优解
{
if(m[i][c] == m[i+1][c])
x[i] = 0;
else
{
x[i] = 1;
c = c-w[i];
}
}
x[n] = (m[n][c])?1:0;//m[n][c]大于0吗?大于0就是选了
return;
}
void main()
{
int i=0;
int w[4]={0,2,3,4};
int v[4]={0,1,2,5};
int x[4]={0};
int array[4][7]={0};
ZeroOneBag(v,w,x,6,3,array);
printf("The most values:%d/n",array[1][6]);
printf("/nWhich objects is selected?/n");
printf("X1,X2,........XN/n");
for(i = 1; i <= 3; i++)
printf("%d/t",x[i]);
printf("/n/n");
}