动态规划-摘自百科
1.最优化原理(最优子结构性质) 最优化原理可这样阐述:一个最优化策略具有这样的性质,不论过去状态和决策如何,对前面的决策所形成的状态而言,余下的诸决策必须构成最优策略。简而言之,一个最优化策略的子策略总是最优的。一个问题满足最优化原理又称其具有最优子结构性质。
2.无后效性 将各阶段按照一定的次序排列好之后,对于某个给定的阶段状态,它以前各阶段的状态无法直接影响它未来的决策,而只能通过当前的这个状态。换句话说,每个状态都是过去历史的一个完整总结。这就是无后向性,又称为无后效性。
3.子问题的重叠性 动态规划将原来具有指数级时间复杂度的搜索算法改进成了具有多项式时间复杂度的算法。其中的关键在于解决冗余,这是动态规划算法的根本目的。动态规划实质上是一种以空间换时间的技术,它在实现的过程中,不得不存储产生过程中的各种状态,所以它的空间复杂度要大于其它的算法。
例子01背包问题:
N件物品和一个容量为j的背包。第i件物品的体积是c[i],价值是w[i]。求解将哪些物品装入背包可使价值总和最大。基本思路这是最基础的背包问题,特点是:每种物品仅有一件,可以选择放或不放。用子问题定义状态:即f[i][j]表示前i件物品恰放入一个容量为j的背包可以获得的最大价值。则其状态转移方程便是:
01背包的状态转换方程 (子问题的重叠性)
f[i,j] = Max{
f[i-1, j-c[i] ]+P[i]( j >= c[i] ), //选择第i件物品,即前i-1件物品在体积为j-c[i]的背包的最大总价值+表示第i件物品的价值
f[i-1, j]//不选择第i件物品
}
f[i,j]表示在前i件物品中选择若干件放在体积为 j 的背包中,可以取得的最大价值。
P[i]表示第i件物品的价值。
现在的决策:为了背包中物品总价值最大化,第i件物品应该放入背包中吗 ?(1放,0不放)
name | volume | value | package's volume | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
a | 2 | 6 | abcde | 0 | 6 | 6 | 9 | 9 | 12 | 12 | 15 | 15 | 15 |
b | 2 | 3 | bcde(a不放结果) | 0 | 3 | 3 | 6 | 6 | 9 | 9 | 9 | 10 | 11 |
c | 6 | 5 | cde(ab不放结果) | 0 | 0 | 0 | 6 | 6 | 6 | 6 | 6 | 9 | 11 |
d | 5 | 4 | d(abc不放结果) | 0 | 0 | 0 | 6 | 6 | 6 | 6 | 6 | 10 | 10 |
e | 4 | 6 | e(abcd不放结果) | 0 | 0 | 0 | 6 | 6 | 6 | 6 | 6 | 6 | 6 |
书包体积 10能装的价值
对于a物品,选择放进
=书包体积8能装的价值(相对于bcde) + a物品价值
对于物品b选择放进
=书包体积6能装的价值(相对于cde)+ b物品价值
对于物品c选择不放进
=书包体积6能装的价值(相对于de)
对于物品d选择不放进
=书包体积6能装的价值(相对于e) == e物品价值
所以: 书包体积 10能装的价值 = a + b + e
例子分析来自:http://blog.csdn.net/mu399/article/details/7722810
附代码:
1 package test; 2 3 public class PackageProblem { 4 static int[][] rArr = new int[5][11];// 存储最优解 ,rArr[i][0]不使用 5 6 public static void main(String[] args) { 7 8 String[] names = { "a", "b", "c", "d", "e" }; 9 int[] volumes = { 2, 2, 6, 5, 4 }; 10 int[] values = { 6, 3, 5, 4, 6 }; 11 int packageVolume = 10; 12 int tail = 4; 13 14 System.out.println("最优解得到的最大价值: " 15 + getBestValue(names, volumes, values, tail, packageVolume)); 16 17 for (int i = 0; i < rArr.length; i++) { 18 for (int j = 0; j < rArr[i].length; j++) { 19 System.out.print(rArr[i][j]); 20 } 21 System.out.println(); 22 } 23 24 getResult(packageVolume, names, volumes); 25 26 } 27 28 static void getResult(int reallyVolume, String[] names, int[] volumes) { 29 System.out.println("最优解: "); 30 int rowIndex = rArr.length - 1; 31 while (reallyVolume > 0) { 32 int nameIndex = 0, packageValue = 0; 33 for (int i = 0; i <= rowIndex; i++) {//取出矩阵中第一次出现的最大值==对应的物品选择放进背包 34 for (int j = 0; j <= reallyVolume; j++) { 35 if (rArr[i][j] > rArr[nameIndex][packageValue]) { 36 nameIndex = i; 37 packageValue = j; 38 } 39 } 40 } 41 System.out.println("nameIndex : " + nameIndex + " packageValue : " 42 + packageValue + " name: " + names[nameIndex]); 43 if (nameIndex != 0) { 44 reallyVolume -= volumes[nameIndex]; 45 } else { 46 reallyVolume = 0; 47 } 48 rowIndex = nameIndex - 1; 49 } 50 51 } 52 53 static int getBestValue(String[] names, int[] volumes, int[] values, 54 int tail, int packageVolume) { 55 if (tail == 0 && packageVolume >= volumes[0]) { 56 return rArr[tail][packageVolume] = values[0]; 57 } 58 int needTailValue = 0; 59 int notNeedTailValue = 0; 60 61 if (tail > 0) { 62 int newPackageVolume = packageVolume - volumes[tail]; 63 if (newPackageVolume >= 0) { 64 if (rArr[tail][newPackageVolume] != 0) {// 减少重复计算 65 needTailValue = rArr[tail][newPackageVolume] + values[tail]; 66 } else { 67 needTailValue = getBestValue(names, volumes, values, 68 tail - 1, newPackageVolume) + values[tail]; 69 } 70 } 71 72 if (rArr[tail][packageVolume] != 0) {// 减少重复计算 73 notNeedTailValue = rArr[tail - 1][packageVolume]; 74 } else { 75 notNeedTailValue = getBestValue(names, volumes, values, 76 tail - 1, packageVolume); 77 } 78 79 } 80 81 return rArr[tail][packageVolume] = Math.max(needTailValue, 82 notNeedTailValue); 83 } 84 85 }
result:
最优解得到的最大价值: 15
00666660606
00009990009
000009900014
000000900014
000000000015
最优解:
nameIndex : 4 packageValue : 10 name: e
nameIndex : 1 packageValue : 4 name: b
nameIndex : 0 packageValue : 2 name: a