动态规划(一):从递归转变为dp ——力扣 983 最低票价

这篇具有很好参考价值的文章主要介绍了动态规划(一):从递归转变为dp ——力扣 983 最低票价。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

 1. 前置知识

(1)动态规划表就是装递归返回值的容器

(2)动态转移方程等价于递归中的尝试策略

(3)递归中各 if 判断条件就是尝试策略的分类,对应了动态转移方程

(4)想象一个二叉树,递归(记忆化)是从顶到底,顶可分出多种情况,而到底就是结束,不会再有分类,因此是从复杂到简单。dp是从底到顶,从最简单的情况入手,当简单情况全部推完,再逐步向复杂情况,因为简单的情况都是复杂情况的分支。

(5)如何判断顶和底,是从前往后还是从后往前,实际是去判断是如何依赖的。dp是从被依赖向依赖的方向填,递归相反。

2. 题目链接

https://leetcode.cn/problems/minimum-cost-for-tickets/description/https://leetcode.cn/problems/minimum-cost-for-tickets/description/

3. 递归(记忆化)版本

int n,l[5]={0,1,7,30},cost[5],days[400],mem[400];

//主函数调用dfs(1)

int dfs(int dep)                      //dep是记录哪一天旅行的数组的下标
{
	int i,k,ans=0x3f3f3f3f;
	if (dep>n)                        //边界条件
		return 0;
	if (mem[dep]!=0)                  //返回记忆值
		return mem[dep];
	for (k=1,i=dep;k<=3;k++)
	{
		while (i<=n && days[dep]+l[k]>days[i])
			i++;
		ans=min(ans,cost[k]+dfs(i));            //递归取最小值
	}
	return mem[dep]=ans;
}

· dfs中核心片段分析

for (k=1,i=dep;k<=3;k++)
{
	while (i<=n && days[dep]+l[k]>days[i])
		i++;
	ans=min(ans,cost[k]+dfs(i));
}

1. 这里就是尝试策略,k代表第几个策略( 1 / 7 / 30天)。

2. 由于是递归版本,旅行第一天的决策会影响后面所有决策,因此循环从1开始往后,从顶到底。

3. · k=1,只管days [ dep ] 这一天,i 一直往后找下一次旅行是在哪一天,对应的days中的下标。

    · k=7,管从days [ dep ] 这一天往后六天,若days中从dep下标往后对应的数值包含在这里面就跳过,直到除了范围。i 一直往后找下一次旅行是在哪一天,对应的days中的下标。

    · k=30 同理 

4. 尤其注意任何判断条件中都必须包含边界条件,不能越界!

4. dp 版本

dp[n+1]=0;							
for (i=n;i>0;i--)
{
	j=i;
	for (k=1;k<=3;k++)
	{
		while (j<=n && days[i]+l[k]>days[j])
			j++;
		dp[i]=min(dp[i],cost[k]+dp[j]);
	}
}
cout<<dp[1];

1. 状态转移方程结构与上述递归版本完全一致,印证了动态转移方程就是尝试策略。

2. ans = min ( ans , cost [ k ] + dfs ( i ) )  变为  dp [ i ] = min ( dp [ i ] , cost [ k ] + dp [ j ] ),印证了动态规划表就是装递归返回值的容器。

3. 思考 是如何判断顶和底(复杂与易)的

想象一棵树,根节点是旅行第一天,每个节点向下分三支,对应三种决策,直到叶节点,就是旅行最后一天,因此猜想从后往前。

再换个角度来看,最简单的是旅行最后一天,只需要考虑自己,后面已经没有了。而不断的往前推,每一个点后面的天数就越多,越复杂。而dp本质就是记录子问题去解决大问题,显而易见是从后往前填答案,最后输出dp [ 0 ]。

5. 总结

无论是递归还是dp,核心都在于找到尝试策略分类,实质是在模拟。dp只是递归的另一种写法,两者并无本质区别。

6. 完整代码

(1)递归版

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;

int n,l[5]={0,1,7,30},cost[5],days[400],mem[400];

int dfs(int dep)
{
	int i,k,ans=0x3f3f3f3f;
	if (dep>n)
		return 0;
	if (mem[dep]!=0)
		return mem[dep];
	for (k=1,i=dep;k<=3;k++)
	{
		while (i<=n && days[dep]+l[k]>days[i])
			i++;
		ans=min(ans,cost[k]+dfs(i));
	}
	return mem[dep]=ans;
}

int main()
{
	int i;
	cin>>n;
	for (i=1;i<=n;i++)
		cin>>days[i];
	for (i=1;i<=3;i++)
		cin>>cost[i];
	cout<<dfs(1);
	return 0;
}

(2)dp版文章来源地址https://www.toymoban.com/news/detail-846214.html

int main()
{
	int i,j,k;
	int dp[400];
	cin>>n;
	memset(dp,0x3f,sizeof(dp));			//这个预处理非常重要,否则后面的min全都是0 
	for (i=1;i<=n;i++)
		cin>>days[i];
	for (i=1;i<=3;i++)
		cin>>cost[i];
	dp[n+1]=0;							//basecase 
	for (i=n;i>0;i--)
	{
		j=i;
		for (k=1;k<=3;k++)
		{
			while (j<=n && days[i]+l[k]>days[j])
				j++;
			dp[i]=min(dp[i],cost[k]+dp[j]);
		}
	}
	cout<<dp[1];
	return 0;
}

到了这里,关于动态规划(一):从递归转变为dp ——力扣 983 最低票价的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 力扣hot100 杨辉三角 递归 DP

    Problem: 118. 杨辉三角 👨‍🏫 参考地址 时间复杂度: 添加时间复杂度, 示例: O ( n ) O(n) O ( n ) 空间复杂度: 添加空间复杂度, 示例: O ( n ) O(n) O ( n )

    2024年01月17日
    浏览(39)
  • 【每日一题Day208】LC1335工作计划的最低难度 | 动态规划

    工作计划的最低难度【LC1335】 你需要制定一份 d 天的工作计划表。工作之间存在依赖,要想执行第 i 项工作,你必须完成全部 j 项工作( 0 = j i )。 你每天 至少 需要完成一项任务。工作计划的总难度是这 d 天每一天的难度之和,而一天的工作难度是当天应该完成工作的最大

    2024年02月05日
    浏览(70)
  • 动态规划(DP)入门——线性DP

    在了解线性DP之前,我们首先要知道什么是动态规划,即为将一种复杂问题,分解成很多重叠的子问题,并通过子问题的解得到整个问题的解的算法。听起来比较抽象,动态规划简单来说就是确定问题的状态,通常题目都会提示,一般为“到第i个为止,xx为j(xx为k)的方案数/最

    2024年02月19日
    浏览(48)
  • 动态规划报告(树形DP+概率DP

    树形 DP,即在树上进行的 DP。由于树固有的递归性质,树形 DP 一般都是递归进行的。一般需要在遍历树的同时维护所需的信息 以一道题目为例 2022CCPC桂林站G Group Homework No, we don’t want group homework. It’s the place where 1+11 can be true. However, you are currently the leader of a group with three

    2024年02月12日
    浏览(48)
  • 【dp动态规划】印章

    共有n种图案的印章,每种图案的出现概率相同。小A买了m张印章,求小A集齐n种印章的概率。 一行两个正整数n和m 一个实数P表示答案,保留4位小数。 2 3 0.7500 python代码

    2024年02月02日
    浏览(61)
  • 动态规划-树形DP

    链接:https://www.acwing.com/problem/content/848/ 给定一颗树,树中包含 n n n 个结点(编号 1 ∼ n 1 sim n 1 ∼ n )和 n − 1 n-1 n − 1 条无向边。 请你找到树的重心,并输出将重心删除后,剩余各个连通块中点数的最大值。 重心定义:重心是指树中的一个结点,如果将这个点删除后,剩

    2024年02月08日
    浏览(40)
  • 动态规划之树形DP

    树形DP是指在“树”这种数据结构上进行的动态规划:给出一颗树,要求以最少的代价(或取得最大收益)完成给定的操作。通常这类问题规模比较大,枚举算法效率低,无法胜任,贪心算法不能求得最优解,因此需要用动态规划进行求解。 在树上做动态规划显得非常合适,

    2024年02月05日
    浏览(45)
  • 动态规划(一)一维DP

    通过上篇文章,动态规划(零)入门概念相信大家已经对动态规划有了一些概念上的理解,那么如何运用动态规划去解决问题呢,首先要知道动态规划的解题步骤。 动态规划的步骤如下: (1) 设计状态 (2) 写出状态转移方程 (3) 设定初始状态 (4) 执行状态转移 (5) 返回最终的解 下

    2024年02月07日
    浏览(40)
  • 动态规划:线性DP

    2024年02月06日
    浏览(41)
  • 算法——动态规划(DP)——递推

    动态规划常用于解决优化问题。 动态规划通常以自底向上或自顶向下的方式进行求解。 自底向上的动态规划从最简单的子问题开始,逐步解决更复杂的问题,直到达到原始问题。 自顶向下的动态规划则从原始问题出发,分解成子问题,并逐步求解这些子问题。 动态规划算法

    2024年01月20日
    浏览(57)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包