在这个暑假过程中,又学习到了很多的算法,发现算法真的是无处不在,很神奇。下面来简单介绍一下动态规划问题。
算法在我们生活中特别常见,例如一个小偷拿着容量为10的背包去商店偷东西,商店里有5种商品,每个商品的(重量,价值)分别为(2,6)、(2,3)、(6,5)、(5,4)、(4,6),小偷怎样才能在不超过背包容量的情况下,偷取价值总和最大的物品呢?
动态规划的思想:
1)最优子结构:如果一个问题的最优解中包含了子问题的最优解,就说该问题具有最优子结构。
2)重叠子问题:是指用来解原问题的递归算法可反复的解同样的子问题,而不总在产生新问题。即当一个递归算法不断地调用同一个问题时,就说该问题包含重叠子问题。
解题步骤:
1)刻画0-1背包问题的最优解结构
2)定义最优解的值
0 ,i=0或w=0
C[i,w]= C[i-1,w] ,wi >w
max{ C[i-1,w-wi] + vi , C[i-1,w] } ,i>0且wi <=w
3)计算背包问题最优解的值
// 0-1背包问题 最优解的值
int[] w = {2, 2, 6, 5, 4}; //物品重量
int[] v = {6, 3, 5, 4, 6}; //物品价值
int c = 10; //背包容量
int[] x = new int[5]; //记录物品装入情况,0表示不装入,1表示装入
x[0] = 1; //初始值表示第一个物品已装入背包
int[][] m = new int[5][c + 1]; //二维表,其中m[i][j]: i个物品背包容量为j时背包的价值为m[i][j]
// 初始化第一行,即背包中装入第一件物品
for (int j = 1; j <= c; j++) {
if (j >= w[0]) {
//为什么要写这一段代码,不直接在下面的代码中将 for (int i = 1; i < 5; i++)中i从0开始?
// (因为 if (j < w[i]) m[i, j] = m[i - 1, j];如果i从0开始,那么m[-1,i],就会越界
m[0][j] = v[0];
}
}
// 背包中依次装入其他的物品
for (int i = 1; i < 5; i++) {
for (int j = 1; j <= c; j++) {
// 当背包容量为j时 小于 第i个物品的重量,第i个物品不能放进去
if (j < w[i]) {
m[i][j] = m[i - 1][j]; //w[i]不装入背包
} else {
// 将第i个物品放进容量为j的背包时,最大价值为: m[i - 1][j - w[i]] + v[i],
// 不将第i个物品放进容量为j的背包时,最大价值为: m[i - 1][j]
if (m[i - 1][j - w[i]] + v[i] > m[i - 1][j]) {
m[i][j] = m[i - 1][j - w[i]] + v[i]; //选择价值较大者
} else {
m[i][j] = m[i - 1][j];
}
}
}
}
System.out.println("背包的最大价值为:" + m[w.length - 1][c]);
重叠子问题:这个是在填写上面这张表格的时候可以很好的体现,例如你算C[2,2]的时候,利用公式展开C[2,2]=max { C[1,0]+6 , C[1,2] },C[1,0]与C[1,2]就不用再算一遍,直接用表格第一行算出来的结果就可以啦。
4)根据计算结果构造问题最优解
判断 C[i,w] , C[i-1,w] 的值是否相等,若相等,则说明Xi=0,不在背包里面,否则,在里面。
特别注意的地方:编号为0的物品不能用上面的公式,计算是否在里面(会出现越界错误),利用m[0,c]=0来判断,若等于0,不在里面。
// 显示最优解
for (int i = 4; i >= 1; i--) {
if (m[i][c] > m[i - 1][c]) {
x[i] = 1; //装入背包
c -= w[i]; //更新背包目前最大容量
} else x[i] = 0; //没有装入背包
}
if (m[0][c] == 0) //判断第一个物品是否放入包中
{
x[0] = 0;
} else {
x[0] = 1;
}
System.out.println("物品的下标:");
for (int i = 0; i < 5; i++) {
if (x[i] == 1) {
//输出最优解
System.out.println(i);
}
}
System.out.println("动态规划的表格:");
for (int i = 0; i < w.length; i++) {
for (int j = 0; j <= 10; j++) {
System.out.print(m[i][j] + " ");
}
System.out.println("--");
}
输出结果为: