动态规划算法

动态规划题目特点:

1. 计数

1)有多少种方式从左上角走到右下角
2)有多少种加法选出k个数和是sum

2. 求最大值最小值

1)从左上角到右下角路径的最大数字和
2)求最长上升子序列长度

3. 求存在性

1)取石子游戏,先手是否必胜
2)能不能选出k个数使和是sum

例题

1. 换硬币 ——求最值动态规划

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

思路:
(以硬币面值分别为2,5,7为例,要求拼出27)
1) 状态
最后一步(最优策略下使用的最后一枚硬币ak
化为子问题(最少的硬币个数拼出更小的面值27-ak)
2)方程
f[x]=min{f[x-2]+1,f[x-5]+1,f[x-7]+1}
3) 初始情况和边界条件
f[0]=0;
如果拼不出x这样的面值,设为无穷大
4)计算顺序
计算顺序确定原则:当我们要用f[x]时,f[x-1] f[x-2]等前面的都已经算过了
故计算顺序是f[0] f[1] f[2]…f[27]

package DP;
public class coin_change {
	//求出满足sum的硬币最小个数
	public static int coin_change(int[] a, int sum) {
		int[] f = new int[sum+1];//动态记录当前面值所需最少硬币数
		f[0]=0;//初始条件:0元最少只需要0枚硬币
		for(int i=1;i<=sum;i++) {//求面值从1到27最少各需要多少硬币
			f[i]=Integer.MAX_VALUE;//初始化当前面值拼不出,记为无穷大
			for(int j=0;j<a.length;j++) {//对不同的硬币面值,依次遍历,选出最优(即和最小)
				if(i-a[j]>=0&&f[i-a[j]]!=Integer.MAX_VALUE) {
					f[i]=Math.min(f[i-a[j]]+1, f[i]);
				}
			}
		}
		if(f[sum]==Integer.MAX_VALUE)
			return -1;
		return f[sum];
	}
	public static void main(String[] args) {
		int [] a = {2,5,7};//硬币面值
		int sum = 27;//要求的硬币面值之和
		System.out.println(coin_change(a, sum));
	}
}

2. 不同的路径——计数型动态规划

问题描述: 有一个机器人的位于一个 m × nm×n 个网格左上角。机器人每一时刻只能向下或者向右移动一步。机器人试图达到网格的右下角。
问有多少条不同的路径?

思路:
1) 状态
最后一步(无论机器人以何种方式到达右下角,最后总有挪动的一步:向右或向下)
化为子问题:到达[m-1,n-1]位置的前一步机器人一定在[m-2,n-1]或者[m-1,n-2],那么,如果机器人有X种方式走到[m-2,n-1],有Y种方式走到[m-1,n-2],那么总共有X+Y种方式走到[m-1,n-1]
2) 方程
f[i][j]=f[i-1][j]+f[i][j-1];
3) 初始条件和边界条件
位于第一行或者第一列的时候,机器人只有一种走法(向下或向右),初始化为1
4)计算顺序
从左到右,从上到下

package DP;

public class UniquePath {
	public static int UniquePaths(int m,int n) {
		int[][] dp = new int[m][n];
		//初始条件:第0行和第0列都只有一种方向可以走
		for(int i=0;i<m;i++) dp[i][0]=1;
		for(int j=0;j<n;j++) dp[0][j]=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];
	}
	public static void main(String[] args) {
		System.out.println(UniquePaths(3, 3));
	}
}

3. 跳跃游戏——存在型动态规划

题目描述: 给出一个非负整数数组,你最初定位在数组的第一个位置。数组中的每个元素代表你在那个位置可以跳跃的最大长度。
判断你是否能到达数组的最后一个位置。

思路:
1) 状态
最后一步:如果青蛙能跳到最后一块石头,我们考虑它跳的最后一步
最后一步是由i跳过来的,i<n-1
这需要两个条件同时满足:

  • 青蛙可以跳到石头i
  • 由石头it跳到n-1需要满足跳跃的距离不超过最大距离 :跳跃的距离:n-1-i 最大距离:a[i] 即n-1-i<=a[i]

子问题:我们需要知道青蛙能否跳到石头i(i<n-1)
设f[j]表示青蛙能否跳到石头j的状态(true/false)

2)方程:
f[j]=OR0<=i<j(f[i]==true AND j-1-i<=a[i])
3) 初始条件和边界条件:
f[0]=true
(无边界条件)
4)计算顺序:
从左到右

package DP;

public class jump_Game {
	public static boolean Jump_Game(int[] a) {
		boolean[] f = new boolean[a.length];
		f[0]=true;
		for(int j=1;j<a.length;j++) {
			for(int i=0;i<j;i++) {
				if(f[i]&&(j-i<=a[i]))
				{
					f[j]=true;
					break;
				}
			}
		}
		return f[a.length-1];
	}
	public static void main(String[] args) {
		int[] a = {2,3,1,1,4};
		System.out.println(Jump_Game(a));
	}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值