动态规划解题步骤详解以及经典入门题

动态规划解题步骤

题目的类型

1.计数型:
1.1多少种方式走到右下角
1.2多少种方式选出K个数字使得和为sum

2.求最大最小值:
2.1从左上角走到右下角的最大数字和
2.2最长上升子序列长度

3.求存在性(博弈)
3.1取石子,先手是否能赢
3.2能不能选出k个数使得和为sum

解题四个步骤:

例题:三种硬币分别为2元,5元和7元,每种硬币足够多,买一本书27块钱,最少需要几个硬币能付清?

动态规划组成部分1:确定状态:

状态在动态规划里面非常的重要,简单的来说就是解动态规划的时候需要开一个数组,数组的每个元素a[i]或者a[i][j]分别代表什么

1.1确定状态两个意识:

1.1.1最后一步
虽然我们不知道最优策略是什么,但是最优策略坑定是K枚硬币a1,a2到ak的面值加起来为27

所以一定有最后一个硬币ak,除掉了这一个硬币,前面的面值就是27-ak;

关键点1:
我们不去关系前k-1枚硬币如何拼出来,甚至我们不知道ak和k的数值,但是我们知道前面的硬币可以拼出27-ak

关键点2:
因为是最优策略,所以拼出27-ak的硬币数一定要最少,否则就不是最优策略

1.2子问题

原问题是用最少的硬币拼出27,所以子问题就是:用最少的硬币可以拼出27-ak,和原问题比,子问题的规模更小。

为了简化定义,所以我们就设状态f(x)=最少用多少枚硬币拼出x。

等等,我们还不知道最后的那枚硬币ak是多少

最后的那枚硬币只可能是2,5,7

如果ak是2,那么f(27)=f(27-2)+1,(加上最后这枚硬币2)
如果ak是5,那么f(27)=f(27-5)+1,(加上最后这枚硬币5)
如果ak是7,那么f(27)=f(27-7)+1,(加上最后这枚硬币7)

所以我们能推导出:
f(27) = min{f(27-2)+1, f(27-5)+1, f(27-7)+1}

那么和递归有什么区别?
在这里插入图片描述
重复计算,时间复杂度非常高。

那么动态规划是怎么避免的:将计算结果保存下来,并改变计算顺序,也就是空间换时间。可以理解为记忆化搜索。

动态规划组成部分2:转移方程:

上面分析了f[x]=最少多少枚硬币可以拼出x

对于任意x,f[x]= min{f[x-2]+1, f[x-5]+1, f[x-7]+1}

动态规划组成部分3:初始条件和边界情况:

问题一:

x-2,x-7,x-5小于0怎么办?
如果拼不出来x,我们就定义f[x]是正无穷,例如f[-1],f[-2]都等于正无穷
所以f[1]=min{f[-1]+1,f[-4]+1,f[-6]+1}=正无穷,表示拼不出来1

问题二:
什么时候停下来?

初始条件f[0]=0,这个必须手动定义,为什么?
假如我们不定义f[0]=0;那么使用转移方程后将会出现:f[0]=min{f[-2]+1,f[-5]+1,f[-7]+1}=正无穷。

动态规划组成部分4:计算顺序:

大多数动态规划都是从0开始,二维的话就是从左到右从上到下来确定。

计算顺序的确定其实很简单:
当我们计算到f[x]的时候,f[x-2]、f[x-5]、f[x-7]都已经得到结果了,所以这里应该从小到大。

这道题我们从0到27,每个位置尝试三种硬币,所以时间复杂度就是273;如果到n,有m种硬币,那么时间复杂度就是mn;远远小于递归。

接下来就是把四个步骤转化为代码:一定要自己憋出来,哪怕是憋几天都别看答案,憋出来了会好很多。

思想很重要但是把思想转化成代码的能力同样重要

上代码

#include <iostream>
#define max 999
using namespace std;
int main()
{
	int a[28]={0};
	int b[3]={2,5,7};
	for(int i=0;i<=27;i++)
	{
		a[0]=0;
		int min=999; 
		for(int j=0;j<=2;j++)
		{
			if(i-b[j]<0&&i!=0)
			{
				a[i]=max;
			}
			else 
			{
				if(a[i-b[j]]+1<min)
			    min=a[i-b[j]]+1;
			}
		}
		a[i]=min;
		cout<<i<<"  "<<a[i]<<endl;
	}
 } 

当然,肯定有更简洁的写法,但我这个写法可读性可能会好一些。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值