luogu1057:传球游戏:记忆化搜索 / DP

题目连接

  • 该题是luogu试炼场的2-12:T3

题目大意

  1. 有n 个小朋友,玩击鼓传花的游戏,小球只能传给左边或者右边的同学;
  2. 要求第m次传回给出发者,问方案数量;

题目分析


解题思路1

  1. 直接DFS,肯定是不能过的,先拿部分分安慰一下自己嘛!

代码1:50分

  • 吃果果的dfs,不做任何优化
//luogu1057:传球游戏 

//50分解题思路:
//直接dfs 
#include<cstdio>

int n,m,ans=0;

void dfs(int x,int t)//当前是 x 号同学, 串了 t次 
{
	if(t==m&&x!=1) return ;//超过 m 次,结束 
	if(t==m&&x==1) //球回到了手上 
	{
		ans++;
		return ;
	}
	
	//右传 
	if(x+1>n) dfs(1,t+1);
	else dfs(x+1,t+1);
	//左传 
	if(x-1==0) dfs(n,t+1);
	else dfs(x-1,t+1);
}

int main()
{
	scanf("%d %d",&n,&m);
	
	dfs(2,1);// 从 1 出发,右传 
	dfs(n,1);// 左传 
	
	printf("%d",ans);
	
	return 0; 
}







思路2:dfs+记忆化

  • 暴搜过不了的题,一般两条路:想剪枝/ 想DP
  • 先想剪枝!!因为已经有了DFS的底板,只是做拓展的思维分析,赛场上的不二之选!!
  • 本题是一个环状结构,会有很多循环的步骤,从这里开始想:
  1. 对于点 i ,剩余步数相同的情况下,答案都是一样的;
  2. 所以用 f 数组表示:第 i 个点,剩下 j 步的情况,减少重复的搜索量;
  3. 记忆化的f数组,帮助回溯的时候更新;

代码2:记忆化搜索

  • 把回溯的内容理解透,才是真正理解递归
//luogu1057:传球游戏:记忆化搜索 

// f[i][j]: 第 i 格, 剩余 j 步的 答案  
#include<bits/stdc++.h>

int n,m,f[50][50];

void dfs(int x,int t)//当前是 x 位置,走了 t步 
{
	f[x][t]=0;//初始化当前位置
	 
	if(x==1&&t==m) //记忆化1:到达 
	{
		f[x][t]=1;
		return ;
	}
	if(t==m) return ;//剪枝 
	
	//搜左边 
	int a; if(x==1) a=n; else a=x-1;
	if(f[a][t+1]==-1) dfs(a,t+1);//左边边界 
	
	//搜右边
	if(f[x%n+1][t+1]==-1) dfs(x%n+1,t+1);//右边没记录

	//回溯更新
	f[x][t]=f[a][t+1]+f[x%n+1][t+1];
}

int main()
{
	memset(f,-1,sizeof(f));
	scanf("%d %d",&n,&m);
	
	dfs(1,0);//从 1 出发,走了 0 步 
	
	printf("%d",f[1][0]);
	
	return 0; 
}







思路3:DP 分析

  • 暴力搜索过不了的题,剪枝可以增加得分量,但如果剩余很多时间(1个小时以上)或者平时练习,还是要掌握相应的DP的思维,多动脑~

  • DP思路: 问什么设什么!

  • f [ i ] [ j ] 表示:第 i 步 走到 j 位置 的方案数,很容易可以想到:任何一个 f[i][j]都是从左和右来的,就是 i-1 步的 j-1和 j+1 位置:

  • 所以通式就可以写成:f[i][j]=f[i-1][j-1]+f[i-1][j+1]

  • 初始化和边界随便搞一下。

代码3:

  • DP代码少,可是你能想到才行~
//luogu1057:传球游戏 

//DP思路: 
//f[i][j]表示:第 i 步 在 j 位置 的方案数 
//任何一个 f[i][j]都是从左和右来的,就是 i-1 步的 j-1和 j+1 位置: 

//所以通式就可以写成
//f[i][j]=f[i-1][j-1]+f[i-1][j+1] 
 
#include<bits/stdc++.h>

int n,m;
int f[50][50];

int main()
{
	scanf("%d %d",&n,&m);
	memset(f,0,sizeof(f));
	
	f[0][1]=1;
	
	for(int i=1;i<=m;i++)//当前第 i 步 
	{
		for(int j=1;j<=n;j++)//当前第 j 个格 
		{
			if(j==1) f[i][j]=f[i-1][n]+f[i-1][2];//左边界 
			else if(j==n) f[i][j]=f[i-1][n-1]+f[i-1][1];//右边界
			else f[i][j]=f[i-1][j-1]+f[i-1][j+1]; 
		}
	}

	printf("%d",f[m][1]);
	
	return 0; 
}


神仙的思路4

推荐一个大佬的思考方式,绝对能帮助你比赛拿高分的那种!

  • 他是用宽搜+打表+DP的详细分析,如果赛场能按照这样的思路来答题,一定可以最少多拿50分!

猜你喜欢

转载自blog.csdn.net/liusu201601/article/details/89469716