动态规划问题学习路线:斐波那契数列,最大递增子序列,松鼠捡苹果,最大公共子序列,字符串编辑距离

本文深入介绍了动态规划的概念,通过硬币凑数、最长递增子序列、松鼠吃苹果和编辑距离等问题,阐述了动态规划的解题思路和方法。文章详细分析了如何定义状态、状态转移方程,以及如何找到最优解,适合初学者逐步学习动态规划的精髓。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

核心:记忆化搜索与递归

下面来看几个具体的问题,由浅入深,逐步学习动态规划的思想

1.斐波那契数列 指的是这样一个数列 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233,377,610,987,1597,2584,4181,6765,10946,17711,28657,46368......

这个数列从第3项开始,每一项都等于前两项之和。
import java.util.Scanner;

public class Fibonacci {
	
	public static int Fib(int n)
	{
		if(n==0 || n==1)  
	    {  
	        return 1;  
	    }  
	    else  
	    {  
	        return Fib(n-1) + Fib(n-2);  
	    }  
	}

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Scanner scanner=new Scanner(System.in);
		int number=scanner.nextInt();
		System.out.println(Fib(number));

	}

}
以上代码在n=5时,fib(5)的计算过程如下:

  1. fib(5)
  2. fib(4) + fib(3)
  3. (fib(3) + fib(2)) + (fib(2) + fib(1))
  4. ((fib(2) + fib(1)) + (fib(1) + fib(0))) + ((fib(1) + fib(0)) + fib(1))
  5. (((fib(1) + fib(0)) + fib(1)) + (fib(1) + fib(0))) + ((fib(1) + fib(0)) + fib(1))
    由上面可以看出,这种算法对于相似的子问题进行了重复的计算,因此不是一种高效的算法。实际上,该算法的运算时间是指数级增长的。 改进的方法是,我们可以通过保存已经算出的子问题的解来避免重复计算:
import java.util.Scanner;
public class Fibonacci {
	final static int MAX=1000;

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Scanner scanner=new Scanner(System.in);
		int number=scanner.nextInt();
		int[] fib=new int[MAX];
		fib[0]=0;
		fib[1]=1;
		for (int i = 2; i <= number; i++) {
			fib[i]=fib[i-1]+fib[i-2];
		}
		System.out.println(fib[number]);

	}

}
将前n个已经算出的前n个数保存在数组map中,这样在后面的计算中可以直接易用前面的结果,从而避免了重复计算。算法的运算时间变为O(n)。

2.找硬币  

如果我们有面值为1元、3元和5元的硬币若干枚,如何用最少的硬币凑够11元? (表面上这道题可以用贪心算法,但贪心算法无法保证可以求出解,比如1元换成2元的时候)

首先我们思考一个问题,如何用最少的硬币凑够i元(i<11)?为什么要这么问呢?两个原因:1.当我们遇到一个大问题时,总是习惯把问题的规模变小,这样便于分析讨论。 2.这个规模变小后的问题和原来的问题是同质的,除了规模变小,其它的都是一样的,本质上它还是同一个问题(规模变小后的问题其实是原问题的子问题)。

好了,让我们从最小的i开始吧。当i=0,即我们需要多少个硬币来凑够0元。由于1,3,5都大于0,即没有比0小的币值,因此凑够0元我们最少需要0个硬币。 (这个分析很傻是不是?别着急,这个思路有利于我们理清动态规划究竟在做些什么。) 这时候我们发现用一个标记来表示这句“凑够0元我们最少需要0个硬币。”会比较方便,如果一直用纯文字来表述,不出一会儿你就会觉得很绕了。那么,我们用d(i)=j来表示凑够i元最少需要j个硬币。于是我们已经得到了d(0)=0,表示凑够0元最小需要0个硬币。当i=1时,只有面值为1元的硬币可用,因此我们拿起一个面值为1的硬币,接下来只需要凑够0元即可,而这个是已经知道答案的,即d(0)=0。所以,d(1)=d(1-1)+1=d(0)+1=0+1=1。当i=2时,仍然只有面值为1的硬币可用,于是我拿起一个面值为1的硬币,接下来我只需要再凑够2-1=1元即可(记得要用最小的硬币数量),而这个答案也已经知道了。所以d(2)=d(2-1)+1=d(1)+1=1+1=2。一直到这里,你都可能会觉得,好无聊,感觉像做小学生的题目似的。因为我们一直都只能操作面值为1的硬币!耐心点,让我们看看i=3时的情况。当i=3时,我们能用的硬币就有两种了:1元的和3元的( 5元的仍然没用,因为你需要凑的数目是3元!5元太多了亲)。既然能用的硬币有两种,我就有两种方案。如果我拿了一个1元的硬币,我的目标就变为了:凑够3-1=2元需要的最少硬币数量。即d(3)=d(3-1)+1=d(2)+1=2+1=3。这个方案说的是,我拿3个1元的硬币;第二种方案是我拿起一个3元的硬币,我的目标就变成:凑够3-3=0元需要的最少硬币数量。即d(3)=d(3-3)+1=d(0)+1=0+1=1. 这个方案说的是,我拿1个3元的硬币。好了,这两种方案哪种更优呢?记得我们可是要用最少的硬币数量来凑够3元的。所以,选择d(3)=1,怎么来的呢?具体是这样得到的:d(3)=min{d(3-1)+1, d(3-3)+1}。

OK,码了这么多字讲具体的东西,让我们来点抽象的。从以上的文字中,我们要抽出动态规划里非常重要的两个概念:状态和状态转移方程。

上文中d(i)表示凑够i元需要的最少硬币数量,我们将它定义为该问题的"状态",这个状态是怎么找出来的呢?

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值