动态规划


动态规划视频链接

动态规划

总结的非常全的各类动态规划问题,一定要看
LintCode阶梯训练,必须先完成上一节课的题目,才能继续下一节课。
LintCode阶梯训练

哪个题目是动态规划

例子1
给定一个矩阵网格,一个机器人从左上角出发,每次可以向下或者向右下走一步

  • 题A
    求有多少种方式走到右下角
  • 题B
    输出所有走到右下角的路径

题A可以用动态规划,题B不可以。

动态规划题目特点

  • 1、计数
    有多少种方式走到右下角。
    有多少种方法选出k个数使得和为sum。

  • 2、求最大最小值
    从左上角到右下角路径的最大数字和
    最长上升子序列长度

  • 3、求存在性
    取石子游戏,先手是否必胜
    能不能选出k个数使得和为Sum

例题

给出不同面额的硬币以及一个总金额. 写一个方法来计算给出的总金额可以换取的最少的硬币数量. 如果已有硬币的任意组合均无法与总金额面额相等, 那么返回 -1.

最小硬币组合–》尽量用面值大的硬币。

动态规划解题步骤

确定状态

状态在动态规划中的作用属于定海神针
简单的说,解动态规划的时候需要开一个数组,数组的每个元素f[i]或者f[i][j]代表什么,类似于解数学题,X、Y、Z代表什么

确定状态需要两个意识

  • 最后一步
    虽然我们不知道最有策略是什么,但是最优策略肯定是K枚硬币a1,a2,a3,…ak面值加起来为Sum
    最后一步即最优策略的最后一步,即这里面的最后一块硬币ak
    除了这个硬币,前面硬币的面值加起来是sum-ak,如果sum为27
    在这里插入图片描述
    对于拼出27-ak的个数一定还是最优的。
    所以我们就要求:最少用多少枚硬币可以拼出27-ak。
    原问题是最少用多少枚硬币拼出27
    我们将问题转化成了一个子问题,而且规模更小:27-ak
    为了简化定义,我们就假设状态f(X)=最少用多少枚硬币拼出X
  • 子问题
    等等,我们还不知道最后那枚硬币ak是多少
    最后那个硬币ak只可能是2、5、7
    如果硬币ak是2,f(27)应该是f(27-2)+1,加1因为要加上这块硬币2
    如果硬币ak是5,f(27)应该是f(27-5)+1,加1因为要加上这块硬币5
    如果硬币ak是7,f(27)应该是f(27-7)+1,加1因为要加上这块硬币7
    除此之外,就没有其他可能了
    需要最少的硬币数,所以
    f(27)=min{f(27-1)+1,f(27-5)+1,f(27-7)+1}

在这里插入图片描述

转移方程

设状态f[x] = 最少用多少枚硬币拼出x。
对于任意x

  • f[x] = min{f[x-2]+1,f[x-5]+1,f[x-7]+1}
    在这里插入图片描述

初始条件和边界情况

  • 初始条件指的是用转移方程算不出来的,但是我们又需要它的定义的。
    在这里插入图片描述

计算顺序

在这里插入图片描述
在这里插入图片描述

递归解法

int f(int x)
{
	if(x==0)
		return 0;
	int res = MAX_VALUE;
	if(x>=2)
	{
		res = Math.min(f(x-2)+1,res);
	}
	if(x>=5)
	{
		res = Math.min(f(x-5)+1,res);
	}
	if(x>=7)
	{
		res = Math.min(f(x-7)+1,res);
	}
	return res;
}

在这里插入图片描述

  • 递归做了很多重复计算,效率低下
  • 如何避免?
  • 将结果保存下来,并改变计算顺序。

小结

在这里插入图片描述

DP解决方法

从集合角度考虑DP问题

动态规划分析的两个阶段

状态表示

  • 状态所表示的集合

状态计算

例子1

给出不同面额的硬币以及一个总金额. 写一个方法来计算给出的总金额可以换取的最少的硬币数量. 如果已有硬币的任意组合均无法与总金额面额相等, 那么返回 -1.

Example
样例1

输入:
[1, 2, 5]
11
输出: 3
解释: 11 = 5 + 5 + 1
样例2

输入:
[2]
3
输出: -1

import java.util.*;
public class Solution {
    /**
     * @param coins: a list of integer
     * @param amount: a total amount of money amount
     * @return: the fewest number of coins that you need to make up
     */
    public int coinChange(int[] coins, int amount) {
        // write your code here
        int[] dp = new int[amount+1];
        dp[0] = 0;
        
        for(int i=1;i<=amount;i++)
        {
            dp[i] = Integer.MAX_VALUE;
            for(int j=0;j<coins.length;j++)
            {
                if(i>=coins[j] && dp[i-coins[j]]!=Integer.MAX_VALUE)
                {
                    dp[i] = Math.min(dp[i-coins[j]]+1,dp[i]);
                }
            }
        }
        if(dp[amount] == Integer.MAX_VALUE)
            dp[amount] = -1;
        return dp[amount];
    }
}

例子2

有一个机器人的位于一个 m × n 个网格左上角。

机器人每一时刻只能向下或者向右移动一步。机器人试图达到网格的右下角。

问有多少条不同的路径?

public class Solution {
    /**
     * @param m: positive integer (1 <= m <= 100)
     * @param n: positive integer (1 <= n <= 100)
     * @return: An integer
     */
    public int uniquePaths(int m, int n) {
        int[][] dp = new int[m][n];
        for(int i=0;i<m;i++)
        {
            dp[i][0] = 1;
        }
        for(int i=0;i<n;i++)
        {
            dp[0][i] = 1;
        }
        for(int i=1;i<m;i++)
        {
            for(int j=1;j<n;j++)
            {
                dp[i][j] = dp[i-1][j]+dp[i][j-1];
            }
        }
        return dp[m-1][n-1];
    }
}

动态规划总结

四个组成部分

  • 确定状态
    研究最优决策的最后一步
    化为子问题
  • 转移方程
    根据子问题定义直接得到
  • 初始条件和边界情况
    细心、考虑周全
  • 计算顺序
    利用之前的计算结果
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值