动态规划算法思想
把待求解问题分解成若干个子问题,先求解子问题,然后由这些子问题的解得到原问题的解,但动态规划求解过的子问题的结果会被保留下来,不像递归那样每个子问题的求解都要从头开始返回求解。
动态规划求解问题的关键在于获得各个阶段子问题的递推关系式:
- 分析原问题的最优解性质,刻画其结构特征。
- 递归定义最优值。
- 自底向上(由后向前)的方式计算最优值。
- 根据计算最优值时得到的信息,构造一个最优解。
动态规划问题的适用条件:
适用动态规划的问题必须满足最优化原理和无后效性。
一、最优化原理(最优子结构性质)
最优化原理可这样阐述:一个最优化策略具有这样的性质,不论过去状态和决策如何,对前面的决策所形成的状态而言,余下的诸决策必须构成最优策略。简而言之,一个最优化策略的子策略总是最优的。一个问题满足最优化原理又称其具有最优子结构性质。
二、无后向性
将各阶段按照一定的次序排列好之后,对于某个给定的阶段状态,它以前各阶段的状态无法直接影响它未来的决策,而只能通过当前的这个状态。换句话说,每个状态都是过去历史的一个完整总结。这就是无后向性,又称为无后效性。
三、子问题的重叠性
动态规划将原来具有指数级复杂度的搜索算法改进成了具有多项式时间的算法。其中的关键在于解决冗余,这是动态规划算法的根本目的。动态规划实质上是一种以空间换时间的技术,它在实现的过程中,不得不存储产生过程中的各种状态,所以它的空间复杂度要大于其它的算法。
0-1 背包问题:
我们有 n 种物品,物品 j 的重量为 wj,价格为 pj。所有物品的重量和价格都是非负的。背包所能承受的最大重量为 c。限定每种物品只能选择 0 个或 1 个。实现计算得出可取的最大价值。
有六件物品,体积和价值见下表,背包容量为 25。
i(物品) | 1 | 2 | 3 | 4 | 5 | 6 |
---|---|---|---|---|---|---|
w(体积) | 11 | 7 | 9 | 12 | 3 | 10 |
v(价值) | 10 | 5 | 7 | 9 | 2 | 12 |
求解的思想:
(1)背包容量不足以放下第 i 件物品,只能选择不拿:m[ i ][ j ] = m[ i-1 ][ j ]
(2)背包容量可以放下第 i 件物品,我们就要考虑拿这件物品是否能获取更大
的价值。
(3)综合以上两种情况可以得到状态转换方程 :
f[i,j]=Max{ f[i-1,j-Wi]+Vi( j >= Wi ),f[i-1,j]
源码:
package backtrack;
/* * 0-1背包问题演示
* @param V 背包容量
* @param N 物品种类
* @param weight 物品重量
* @param value 物品价值
* @return
*/
public class ZeroOneKnapsackBacktrack {
public static void main(String[] args){
int V=25; //背包空间
int N=6; //物品个数
int[] weight={11,7,9,12,3,10}; //物品体积
int[] value={10,5,7,9,2,12}; //物品价值
//初始化动态规划数组
int[][] dp = new int[N+1][V+1];
//为了便于理解,将dp[i][0]和dp[0][j]均置为0,从1开始计算
for(int i=1;i<N+1;i++){
for(int j=1;j<V+1;j++){
//如果第i件物品的重量大于背包容量j,则不装入背包
//由于weight和value数组下标都是从0开始,故注意第i个物品的重量为weight[i-1],价值为value[i-1]
if(weight[i-1] > j)
dp[i][j] = dp[i-1][j];
else
dp[i][j] = Math.max(dp[i-1][j],dp[i-1][j-weight[i-1]]+value[i-1]);
}
}
//则容量为V的背包能够装入物品的最大值为
int maxValue = dp[N][V];
//逆推找出装入背包的所有商品的编号
int j=V;
String numStr="";
for(int i=N;i>0;i--){
//若果dp[i][j]>dp[i-1][j],这说明第i件物品是放入背包的
if(dp[i][j]>dp[i-1][j]){
numStr = i+","+numStr;
j=j-weight[i-1];
}
if(j==0)
break;
}
System.out.println("最大价值:"+maxValue+"\n"+"装入的物品编号是:"+numStr);
}
}
算法分析
时间复杂度:O(nc),n为物品数,c为背包容量。当背包容量c很大时,算法需要的计算时间较多。例如,当c>2n时,算法需要Ω(n2n)计算时间。
空间复杂度:O(nc),n为物品数,c为背包容量。