动态规划DP

目录

前言

跳台阶

打家劫舍

数字三角形


前言

      拖鞋言:不得不说,我可真是个废物。算法从去年四月开始接触,期间一直走走停停。几乎没学到什么实在的东西。壮志凌云地报名了蓝桥杯,但到此为止却并无实际进展。接下来计划静下心来好好研究算法,并持续高质量记录学习过程。祝顺利~

        此篇文章是跟随下面视频学习的笔记。up使用 dfs暴力 --> 记忆化搜索 --> 递推 的思路来给我这种小白讲动态规划,我觉得真的很妙。推荐观看。动态规划(dp)入门 | 这tm才是入门动态规划的正确方式! | dfs记忆化搜索 | 全体起立!!_哔哩哔哩_bilibili

跳台阶

821. 跳台阶 - AcWing题库

问题:一次可以跳上1级台阶,也可以跳上2级。求该跳上一个n级的台阶总共有多少种跳法。

这个问题看似简单,其实并不那么容易理解。拖鞋起初学习裴波那契 接触它的时,也死活理解不了。

问题思路:从问题里,首先我们不难知道,如果只有一级台阶,那么只有一种方案,即只走一步。如果只有两级台阶,我们也很快分析出来只有两种方案,即直接跳两级,或是一级一级来。那么有三级台阶时,首先我们会面临一个选择。第一步,是走一级,还是走两级。如果我们第一步走一级,那么就剩下两级,这剩下的两级我们可以直接两步跨完,也可以一级一级走。“可以直接两步跨完,也可以一级一级走”这句话是不是有些熟悉,也就是只有两级台阶时,我们的处理方法。也就是说三级台阶问题可以被我们分解成一级+两级台阶问题。(此处可能依旧会有朋友不能理解,可以去看看别的文章的解释,或许慢慢就能明白了。)而四级台阶,也会面临选择。第一步是走一级,还是两级。倘若走两级,那么就剩下两级(又变成了只有两级台阶的问题)。倘若走一级,则剩下三级,是只有三级台阶的问题(而上面我们又将三级台阶,分成了一级+二级台阶的问题)。有点小饶,但此时我们可以发现有点像套娃,大问题会变成小问题,最终会有一个已知值。

使用dfs暴力算法的方法解答如下:

#include<stdio.h>
int dfs(int n)
{
	if(n==1) return 1;    //一级台阶时
	else if(n==2) return 2;    //两级台阶时
	else return dfs(n-1)+ dfs(n-2);  //分解  
}

int main()
{
	int n;
	scanf("%d",&n);
	int res = dfs(n); //通过dfs来暴力求解
	printf("%d\n",res);
	return 0;
 } 

记忆化搜索:

为何要使用记忆化搜索?补齐上面的递归树,我们会发现蓝圈圈内重复地去查找了三级台阶的情况。虽然在这个例子里只重复了一个环节,但倘若n极大时,我们就会发现重复递归的太太太多,从而导致超时问题。然后就出现了“记忆化搜索”。什么是记忆化搜索呢?其实就是把计算过的答案存下来。

#include<stdio.h>
int men[20];  //定义一个men来存计算过的值
int dfs(int n)    
{
	if(men[n]) return men[n]; //如果有值,则直接返回,无需重复计算
	if(n==1) return 1;
	else if(n==2) return 2;
	else return men[n]= dfs(n-1)+ dfs(n-2); 
}

int main()
{
	int n;
	scanf("%d",&n);
	int res = dfs(n);
	printf("%d\n",res);
	return 0;
 } 

递推dp:

#include<stdio.h>
int main()
{
	int n,i;
	int f[20];
	scanf("%d",&n);
	f[1]=1,f[2]=2;
	for(i=3;i<=n;i++)
	{
		f[i]=f[i-1]+f[i-2]; //此为dfs的状态转移公式
	}
	printf("%d",f[n]);	
	return 0;
 } 

打家劫舍

198. 打家劫舍 - 力扣(LeetCode)

问题:

你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警

给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。

输入样例:

4

10 7 6 14

输出样例:

24

问题思路: 我们以上述的输入样例为参考画出下图。对于第一家店,阿福有两种可能性一种是偷,一种是不偷。当不偷时,我们顺延考虑第二家店。当偷时,按照题目要求我们只能考虑第三家店或者第四家店。与跳台阶不同,此题需求一个max,使得阿福的利益最大。即类似于求最佳路径。我们的思路应该是,递归每一种可能性,然后比较哪种可能性的利益最大。

 dfs实现如下:

#include<iostream>
#include<algorithm>
#include<cstring>

using namespace std; 

int n,T,i;
int home[100010];

int dfs(int x)
{
	if(x>n) return 0;
	else return max(dfs(x+1),dfs(x+2)+home[x]);
}

int main()
{
		scanf("%d",&n);
		for(i=1;i<=n;i++)
		{
			scanf("%d",&home[i]);
		}
		int res = dfs(1);
		printf("%d\n",res);
	}
	return 0;
}

记忆化搜索:

#include<iostream>
#include<algorithm>
#include<cstring>

using namespace std; 

int n,i;
int home[100010];
int men[100010];

int dfs(int x)
{
	if(men[x]) return men[x];
	int sum = 0; 
	if(x>n) sum =0;
	else sum= max(dfs(x+1),dfs(x+2)+home[x]);
	
	men[x]=sum;
	return sum;
}

int main()
{
		scanf("%d",&n);
		for(i=1;i<=n;i++)
		{
			scanf("%d",&home[i]);
		}
		int res = dfs(1);
		printf("%d\n",res);
	return 0;
}

递推:(倒序)

#include<iostream>
#include<algorithm>
#include<cstring>

using namespace std; 

int n,i;
int home[100010];
int f[100010];

int main()
{
		scanf("%d",&n);
		for(i=1;i<=n;i++)
		{
			scanf("%d",&home[i]);
		}
		memset(f,0,sizeof f);
		for(i=n;i>=1;i--)
		{
			f[i]=max(f[i+1],f[i+2]+home[i]);
		}
		printf("%d\n",f[1]);
	return 0;
}

递推(正序):

#include<iostream>
#include<algorithm>
#include<cstring>

using namespace std; 

int n,i;
int home[100010];
int f[100010];

int main()
{
		scanf("%d",&n);
		for(i=1;i<=n;i++)
		{
			scanf("%d",&home[i]);
		}
		memset(f,0,sizeof f);
		for(i=1;i<=1;i++)
		{
			//f[i]=max(f[i-1],f[i-2]+home[i]);
            f[i+2]=max(f[i+1],f[i]+home[i]); //i-2时会越界,所以下标同时映射(+2)
		}
		printf("%d\n",f[n+2]);
	return 0;
}

数字三角形

P1216 [USACO1.5] [IOI1994]数字三角形 Number Triangles - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

问题:

写一个程序来查找从最高点到底部任意处结束的路径,使路径经过数字的和最大。每一步可以走到左下方的点也可以到达右下方的点。

在上面的样例中,从 7→3→8→7→57→3→8→7→5 的路径产生了最大权值。

问题思路:根据题目的意思,我们需要找到数字和最大的那条路径。对于每一个点来说,有两个选择,一个是往下走,一个是往下右走(据输入格式而言)。倘若我们设数字7这个点的坐标为[i,j],倘若往下走坐标即变成[i+1,j],倘若往下右走坐标即变成[i+1,j+1]。(此处不理解,可以画出一个二位数组,对比一下)。后续的思路和上一道题(大盗阿福)是一致的。都是遍历每一种可能性,并比较最大值。

dfs: 

#include<iostream>
#include<algorithm>
#include<cstring>

using namespace std;
int n,i,j;
int f[1005][10005];
int dfs(int x,int y)
{
	if(x>n||y>n) return 0;
	else return max(dfs(x+1,y),dfs(x+1,y+1))+f[x][y];
}

int main()
{
	scanf("%d",&n);
	for(i=1;i<=n;i++)
	{
		for(j=1;j<=i;j++)
		{
			scanf("%d",&f[i][j]); 
		}
	}
	int res = dfs(1,1);
	printf("%d",res);
	return 0;
 } 

记忆化搜索:

#include<iostream>
#include<algorithm>
#include<cstring>

using namespace std;
int n,i,j;
int f[1005][1005];
int men[1005][1005];
int sum =0;
int dfs(int x,int y)
{
	if(men[x][y]) return men[x][y];
	if(x>n||y>n) return sum =0;
	else sum= max(dfs(x+1,y),dfs(x+1,y+1))+f[x][y];
	men[x][y] = sum;
	return sum;
}

int main()
{
	scanf("%d",&n);
	for(i=1;i<=n;i++)
	{
		for(j=1;j<=i;j++)
		{
			scanf("%d",&f[i][j]); 
		}
	}
	int res = dfs(1,1);
	printf("%d",res);
	return 0;
 } 

递推(倒序):

#include<iostream>
#include<algorithm>
#include<cstring>

using namespace std;
int n,i,j;
int g[1005][1005];
int f[1005][1005];

int main()
{
	scanf("%d",&n);
	for(i=1;i<=n;i++)
	{
		for(j=1;j<=i;j++)
		{
			scanf("%d",&g[i][j]); 
		}
	}
	for(i = n;i>=1;i--)
	{
		for(j=1;j<=n;j++)
		{
			f[i][j] = max(f[i+1][j],f[i+1][j+1])+g[i][j];
		}
	}
	printf("%d",f[1][1]);
	return 0;
 } 

递推(正序):

#include<iostream>
#include<algorithm>
#include<cstring>

using namespace std;
int n,i,j;
int g[1005][1005];
int f[1005][1005];

int main()
{
	scanf("%d",&n);
	for(i=1;i<=n;i++)
	{
		for(j=1;j<=i;j++)
		{
			scanf("%d",&g[i][j]); 
		}
	}
	for(i = 1;i<=n;i++)
	{
		for(j=1;j<=n;j++)
		{
			f[i][j] = max(f[i-1][j],f[i-1][j-1])+g[i][j];
		}
	}
	
	int res =0;
	for(j =1;j<=n;j++) //正推时,会有很多个结果,需要循环判断哪个最大。 
	{
		res = max(f[n][j],res);
	}
	printf("%d",res);
	return 0;
 } 

拖鞋言: 下期文章,记录动态规划的习题~

  • 15
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值