动态规划 只需一题让你彻底理解精髓

动态规划又名为记忆化搜索,就是空间换时间,可以让代码更加高效的运行,换句话说就是,可以暴力解出来的东西,都可以用动态规划来简化运算,下面的代码与讲解就默认为你们对动态规划有一定的了解,对深搜DFS,也有运用经验,只是进阶一下,让你更好的理解。

关于动态规划的题型,有以下几类适用:

1.坐标类 20%

2.序列类 20%

3.划分类 20%

4.区间类 15%

5.背包类 10%

6.最长序列类 5%

7.博弈类 5%

8.综合类 5%

而动态规划的基本就是无非

1.计数(有多少种策略,方法)

2.求最值(最长不连续,连续,递增,递减序列)

3.求是否存在(选择其中几个,是否可以拼出来)

下面开始具体的讲解,大家要是想要更新那个具体的专题,可以评论和我说一声,我一定会尽快更新!!

题目1:

机器人运动 当前为start 移动K 目标 aim 总长度 N 求从start到aim一共有多少种方案 只要不是在终点位置 可以左右移动一格

#include <bits/stdc++.h>
using namespace std;
//机器人运动 当前为start 移动K 目标 aim 总长度 N 求从start到aim一共有多少种方案 只要不是在终点位置 可以左右移动一格
// 
int K,N;
int start,aim; 
int ans;
int dfs(int s,int k)//当前在s这个位置 还要剩余步骤k 
{
	if (k==0)// 没有步数了 走不动了 查看当前位置 
	{
		if (s==aim) return 1; 
		else return 0;
	}
	if (s==1) return dfs(2,k-1);
	if (s==N) return dfs(N-1,k-1);
	else return dfs(s+1,k-1)+dfs(s-1,k-1);//加上向左向右走的 种数 
} 

int main()
{
	cin >> start >> aim >> K >> N;
	cout << dfs(start,K);//还需K步 当前位置为start 
	return 0;
 } 

完全的暴力搜索,分解为子问题之后就好分析了。下面进行一些优化。

记忆化搜索:(简单分析)

#include <bits/stdc++.h>
using namespace std;
//机器人运动 当前为start 移动K 目标 aim 总长度 N 求从start到aim一共有多少种方案 只要不是在终点位置 可以左右移动一格 
int K,N;
int start,aim; 
int dp[100][100];//s位置 k剩余步数 存在的方法数 
int dfs(int s,int k)//当前在s这个位置 还要剩余步骤k 
{
//	cout << s << " " << k <<" " << dp[s][k] <<endl;
	if(dp[s][k]) return dp[s][k]; 
	int res = 0; 
	if (k==0)// 没有步数了 走不动了 查看当前位置 
	{
		if (s==aim) res=1; 
	}
	else if (s==1) res = dfs(2,k-1);
	else if (s==N) res = dfs(N-1,k-1);
	else res = dfs(s+1,k-1)+dfs(s-1,k-1);
	dp[s][k]=res;
	return res;
} 
//上面其实有大量的重复计算 每次参数只要s,k相同
// 得出的结果都是一样的 因此可以将得到的结果提前存下来 这样下次调用就可以很方便了 
///用二维数组dp存储 

//未优化的时间 
//1 10 15 20
//350
//--------------------------------
//Process exited after 8.267 seconds with return value 0

//Process exited after 1.786 seconds with return value 0 动态规划 方法二的时间 
int main()
{
	cin >> start >> aim >> K >> N;
	dfs(start,K);
	cout << dp[start][K];//还需K步 当前位置为start 
	return 0;
 } 

可以明显看出来 效率快了好多 这就是记忆化搜索的魅力,算过一次就没有必要去算了,保存下来,下次遇到,直接调用即可。

下面写的是用动态转移方程写的,对于新手而言,不需要关注动态转移方程是怎么来的,你是想不来的,动态转移只是一种结果,相当于重要的是上面的分析,上面的尝试,然后在熟练的=后而归纳总结的一个方程,过于关注动态转移方程,不去亲手实践,不去模拟,是不可能学好动态规划的。看似很酷的方程后面的努力才是最多的;

#include <bits/stdc++.h>
using namespace std;
int K,N;
int start,aim; 
int dp[100][100];//s位置 k剩余步数 存在的方法数 
int dfs(int s,int k)//当前在s这个位置 还要剩余步骤k 
{
	for (int j = 1;j<=k;j++)//剩余的步数 
	{
		dp[aim][0] = 1;//位置为aim 且当前步数为0才是1 除此步数为0的都为0 
		for (int i = 1;i<=N;i++)//当前处理的位置 
		{
			if (i==1)//位置为1 
			{
				dp[i][j]= dp[2][j-1];
			 } else if (i==N)
			 {
			 	dp[i][j]=dp[N-1][j-1];
			 }else dp[i][j] = dp[i+1][j-1] + dp[i-1][j-1];
		}
	} 	
	return dp[s][k];
} 
//未优化的时间 
//1 10 15 20
//350
//--------------------------------
//Process exited after 8.267 seconds with return value 0

//Process exited after 1.786 seconds with return value 0 动态规划 方法二的时间 

//Process exited after 1.822 seconds with return value 0 方法三 时间也差不多 
int main()
{
	cin >> start >> aim >> K >> N;
	cout << dfs(start,K);
	return 0;
 } 

 

 上面的是 start = 2,aim = 4,K = 6,N = 5;

 画完图存储的就是这些数据,可以直接查询出来,而求值直接用for循环解决。理论上比第二种递归要快,然后代码可以更简洁一些。

多去画画,列一些式子,找找规律。

#include <bits/stdc++.h>
using namespace std;
int K,N;
int start,aim; 
int dp[100][100];//s位置 k剩余步数 存在的方法数 
int dfs(int s,int k)//当前在s这个位置 还要剩余步骤k 
{
	dp[aim][0]=1;//步骤为0已经讨论完毕 
	for (int j = 1;j<=k;j++)//剩余的步数 范围为1~K 
	{
		dp[1][j] = dp[2][j-1];//特殊处理 不要进循环 然后判断了 
		for (int i = 2;i<N;i++)//当前处理的位置 
		{
 			dp[i][j] = dp[i+1][j-1] + dp[i-1][j-1];
		}
		dp[N][j]=dp[N-1][j-1];
	} 	
	return dp[s][k];
} 
//1 10 15 20
//350
//--------------------------------
//Process exited after 8.267 seconds with return value 0 方法一 未优化的时间 

//Process exited after 1.786 seconds with return value 0 方法二的时间 动态规划

//Process exited after 1.822 seconds with return value 0 方法三 时间也差不多 

//Process exited after 1.429 seconds with return value 0 方法四 最优时间 
int main()
{
	cin >> start >> aim >> K >> N;
	cout << dfs(start,K);
	return 0;
 } 

上面的应该是算作正式的动态规划,用了动态转移方程。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值