求最值型动态规划入门

本文根据B站up九章算法,视频【动态规划专题班】ACM总冠军、清华+斯坦福大神带你入门动态规划算法。

先看一个例题,本文围绕例题来讲解(LintCode第669题)
在这里插入图片描述

动态规划组成部分:

部分1:确定状态

一般来说,做动态规划问题时一般需要开一个数组,状态就是数组的每个元素a[i]或a[i][j]代表什么。

确定状态需要两个意识:

(1). 最后一步

在这里插入图片描述

(2). 子问题

在这里插入图片描述
但是我们不知道最后一枚硬币具体的值,而问题有需要返回最优解,所以
f(27)=min{f(27-2)+1,f(27-5)+1,f(27-7)+1}
在这里插入图片描述
此时时使用递归也可以得出结果:

#include<bits/stdc++.h>
#include<algorithm>
#include<math.h>
using namespace std;
int f(int x);
int main(){
	int ans=f(27);
	cout<<ans;
}
int f(int x){
	if(x==0)return 0;
	int ans=1000000;
	if(x>=2)
		ans=min(f(x-2)+1,ans);
	if(x>=5)
		ans=min(f(x-5)+1,ans);
	if(x>=7)
		ans=min(f(x-7)+1,ans);
	return ans;
}

但是递归做了很多重复的计算,效率低下:
在这里插入图片描述
而动态规划则是 将计算结果保存下来并改变计算顺序

部分2:转移方程

在这里插入图片描述
注意:再写转移方程时,子问题和最后一步在第一步确定状态时都已经做出
在写转移方程时

部分3:初始条件和边界情况

(1)初始条件

初始条件既人为定义那些用转移方程算不出来的结果
在例题中比如 f[0] :如果凑0元则需要0个硬币,所以给定初始条件:f[0]=0;
此后的每一项都能推出,比如f[2]=f[2-2]+1=1;
注意:
初始条件类似于递归最深的一层,需要有确定的数,之前的每一层才能都有返回值

(1)边界情况

边界情况即预防数组越界等情况。
在例题中,比如求凑-2元需要多少个硬币,此时我们让f[-2]返回正无穷大。

部分4:计算顺序

确定计算顺序只有一个原则:
那就是在计算到某一项时,他所需要的之前项都已经计算出来了
大部分都是从小到大。

例题中,拼出x元的硬币:f[x]=min{f[x-2]+1,f[x-5]+1,f[x-7]+1}
初始条件:f(0)=0
然后计算f[1],f[2],f[3],f[4]…f[27]
在我们计算f[x]时,他所需要的f[x-2],f[x-5],f[x-7]都已经计算出来了
在这里插入图片描述
不难看出,动态规划做此例题时间复杂度比递归要小的多。

  1. 每一步尝试三种硬币,一共27步。
  2. 与递归相比没有任何重复计算。
  3. 算法的时间复杂度(既需要进行的步数):273(如果计算n元、m种硬币,则时间复杂度为n,

小结

在这里插入图片描述

例题题解代码:

#include<bits/stdc++.h>
#include<algorithm>
#include<math.h>
using namespace std;
int f[28]={0};
int a[3]={2,5,7};
int dp(int x);
int main(){
	int ans=dp(27);
	cout<<ans;
}
int dp(int x){
	f[0]=0;//初始条件 
	for(int i=1;i<=x;i++){
		f[i]=10000000;//初始化数组 
		for(int j=0;j<3;j++){//最后一步 
			if(i>=a[j]&&f[i-a[j]]!=10000000)
			//这一步判断是为了防止
			//1:要拼的钱数小于硬币的最大面值
			//2;剩下的硬币数无法拼成,比如27元可以用22+一个5来拼成,但如果22拼不出来,则需要跳过这种情况 
			f[i]=min(f[i-a[j]]+1,f[i]);
		}
	}
	if(f[x]==10000000){
		f[x]=-1;
	} 
	return f[x];
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值