动态规划算法

通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。动态规划常常适用于有重叠子问题最优子结构性质的问题。

1.基本思想

    若要解一个给定问题,我们需要解其不同部分(即子问题),再合并子问题的解以得出原问题的解。 通常许多子问题非常相似,为此动态规划法试图仅仅解决每个子问题一次,从而减少计算量: 一旦某个给定子问题的解已经算出,则将其记忆化存储,以便下次需要同一个子问题解之时直接查表。 这种做法在重复子问题的数目关于输入的规模呈指数增长时特别有用。

2.问题特征

(1)重叠子问题:在用递归算法自顶向下解问题时,每次产生的子问题并不总是新问题,有些子问题被反复计算多次。动态规划算法正是利用了这种子问题的重叠性质,对每一个子问题只解一次,而后将其解保存在一个表格中,在以后尽可能多地利用这些子问题的解。
(2)最优子结构:当问题的最优解包含了其子问题的最优解时,称该问题具有最优子结构性质。
(3)无后效性:这是DP中最重要的一点, 他要求每个子问题的决策不能对后面其他未解决的问题产影响, 如果产生就无法保证决策的最优性, 这就是无后效性。

3.常用的解题步骤

   第一步:确定子问题。 在这一步重点是分析哪些变量是随着问题规模的变小而变小的, 哪些变量与问题的规模无关。
   第二步:确定状态:根据上面找到的子问题来给你分割的子问题限定状态
   第三步:推到出状态转移方程:这里要注意你的状态转移方程是不是满足所有的条件, 注意不要遗漏。
   第四步:确定边界条件:先根据题目的限制条件来确定题目中给出的边界条件是否能直接推导出, 如果不行也可以尝试从边界条件反推(举个例子:a(n)→a(2)有递推关系, 但是a(2)→a(1)不符合上述递推关系, 我们就可以考虑用a(1)来倒推出a(2), 然后将递推的终点设置为a(2));
   第五步:确定实现方式:这个依照个人习惯 就像是01背包的两层for循环的顺序 
   第六步:确定优化方法:很多时候你会发现走到这里步的时候你需要返回第1步重来。首先考虑降维问题(优化内存), 优先队列、四边形不等式(优化时间)等等。 

4.分治与动态规划

   共同点:二者都要求原问题具有最优子结构性质,都是将原问题分而治之,分解成若干个规模较小(小到很容易解决的程序)的子问题.然后将子问题的解合并,形成原问题的解.

不同点:分治法将分解后的子问题看成相互独立的,通过用递归来做。
     动态规划将分解后的子问题理解为相互间有联系,有重叠部分,需要记忆,通常用迭代来做。

5.个人总结

  由于最近在找实习,刷了一些笔试题,遇到了蛮多需要使用动态规划方法求解的题目。初次遇到这类题目,感觉束手无策,无从下手,因为很难把握这种方法的精髓在哪,重点是什么。起初,遇到这类题目,我都是先看别人的答案和分析,然后自己慢慢思考和总结。通过一段时间的学习,渐渐明白和这类方法的特性。
  其实,动态规划方法的本质就是记忆化搜索,什么是记忆化搜索?就是在暴力搜索的基础上,把每个中间结果保存下来,在需要用到的时候直接取出即可,不需要重新计算,这是在以空间换时间。还有动态规划通常使用迭代方法进行搜索而不是使用递归进行搜索。
  在做过的笔试题目中,大部分都是用一个二维数组来存储记忆搜索的结果。所以,针对笔试中的动态规划问题,总结出以下步骤:
  (1)根据题目的规模定义一个二维数组dp,并明确数组中元素的意义;
  如,在背包问题中,dp[i][j]表示前i个物体放入容量为j的背包的最大价值
 (2)确定状态转移方程。
  如:dp[i][j]=max{dp[i-1][j-c[i]]+w[i] , dp[i-1][j]}
 (3)考虑边界条件,并写出代码实现
 以上纯属个人总结,如有不正确的地方请在评论区指正。

6.典型问题——背包问题

   有N件物品和一个容量为V的背包。第i件物品的费用是c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。
public class Solution{

   public int backpack(int [] c,int [] w,int v){

        int m=c.length;
        int [][] dp=new int [m+1][v+1];//dp[i][j]表示前i个物品放入容量为j的背包中价值的最大值

        //初始化dp数组
        for(int i=0;i<m+1;i++) {
            dp[i][0]=0;
        }
        for(int i=0;i<v+1;i++) {
            dp[0][i]=0;
        }

        //动态规划过程
        for(int i=1;i<m+1;i++) {
            for(int j=1;j<v+1;j++) {
                //当物品为i件重量为j时,如果第i件的重量(c[i-1])小于重量j时,dp[i][j]为下列两种情况之一:
                //(1)物品i不放入背包中,所以dp[i][j]为dp[i-1][j]的值
                //(2)物品i放入背包中,则背包剩余重量为j-c[i-1],所以dp[i][j]为dp[i-1][j-c[i-1]]的值加上当前物品i的价值w[i]
                if(c[i-1]<j) {
                    dp[i][j]=Math.max(dp[i-1][j], dp[i-1][j-c[i]]+w[i]);
                }else {
                    dp[i][j]=dp[i-1][j];
                }
            }
        }


        return dp[m][v];
    }
}

参考博客:
我是王小北
sun897949163
凌风1205

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值