[刷题之旅no26]P3956 [NOIP2017 普及组] 棋盘

本文详细介绍了使用深度优先搜索(DFS)解决棋盘行走问题的过程,包括思路、问题及解决方案。在初始实现中,遇到死递归导致超时,通过引入记忆化搜索(使用二维数组记录最优解)成功优化了算法,最终通过测试并得出正确结果。文章强调了在大规模数据下优化DFS的重要性,并展示了如何通过记录和避免重复路径来提高效率。
摘要由CSDN通过智能技术生成

1.走棋盘
同色直接走
异色花一金
无色可魔法
魔法花两金
不可连续魔
要求最小金
不通为负一
思路:
1.用深搜,设置参数,四个,坐标xy,是否可以使用魔法,当前金币数量
根据当前位置判断可以走的方向,然后根据情况递归即可。
问题:
1.如何确定施加魔法的颜色
2.如何执行施加魔法之后颜色消失这一步。
3.如何防止重复绕圈,也就是如果我按照当前位置四个方向进行递归,肯定会出现重复路线
解决问题:
3.每次进入函数,将当前坐标相应位置标记为1.
然后递归。
当递归结束之后,把相应位置标记为0;
return;
//递归棋盘的标准方法
1.如何确定施加魔法的颜色
实际上,我们不用确定到底施加什么颜色
如果当前位置四周出现空位置,
那么在我们递归这个空位置时,再判断四周情况
如果还是空位置,那就无法继续递归了
如果有红色,那么就当作我们施加了红色魔法,不花钱直接进入
如果有黄色,那么就当作我们施加了黄色魔法,不花钱直接进入
这样随机颜色,顺便把我们的2问题也解决掉了。嘻嘻
预处理:我比较喜欢初始化数组为0,那么就和题目中要求的红为0冲突,所以我把题目中的红色改成2
然后把数组开大一点,设置城墙为3,防止出现越界问题
递归函数细则:
1.标记当前点
如果当前点==m,m即到达终点
那么和最小gold进行比较,如果比最小gold小,更新
并且把当前点清0。
return;
遍历四周点:
分别判断以下情况
4.城墙或者已经走过了
不可以递归
1.空的
看当前是否可以使用魔法
能则递归,金币加2,magic改成0“不可使用”
2.红色
判断当前是否可以使用魔法

看当前方方块颜色
红色,直接走,金币不变,magic不变
黄色,直接走,金币加1,magic不变
不能
说明当前这个方块就是我用魔法变来的
所以我选红色
直接递归,金币不变,magic改成1
3.黄色
和红色同理
城墙和已经走过了放在最前面,因为这是不符合的情况
如果四周点都递归完成
取消标记当前点
return即可;
好的,目前出现了一个小逻辑错误
测试样例中的8我打印出来7
现在需要测试寻找问题
逻辑出现问题
也就是说
如果我当前所在位置为黄色
隔着一块空地为红色
如果按照我原来的思路
将其随机
那么如果是红色
我需要花费2+1
如果是黄色
我也需要2+1;
而我想的是,只花费2
新思路解决2.3问题
如果可以施加魔法
保证施加魔法所出现的颜色和当前所站位置颜色相等。
这样可以保证花金币最少
递归,金币+2;
递归结束之后,把这一块颜色归0即可
改变这个逻辑思路,其它都要改变
比如
遍历四周遇到红色:
1.如果当前可以使用魔法
那么判断当前位置颜色
红色,直接走,magic=1;
黄色,金币+1,magic=1;
2.如果不可以使用魔法
判断当前位置
红色,直接走,magic=1;
黄色,金币+1,magic=1;
前后一样,故合并
啊偶
深搜TLE了,不过思路没问题,我还是很高兴的
我咋感觉是出现了死递归问题呢?程序输入之后一直没结果,我需要检查一下了
感觉题目一直在搜索一些没有意义的坐标
分支不会多到数不过来啊!
看题解里面也有人使用dfs方法,到底是怎么回事导致我的递归死亡了呢
学习一下大佬的思想吧,还是不太明白到底为什么会递归那么多次
突然想起来,在基数非常大的时候,递归的次数上升会非常快
此时如果想要优化dfs,则需要用到记忆化搜索。
记忆化搜索,是我之前的遗留问题,现在还是要解决掉啊!
设置一个数组,记录1,1到x,y的最优解f[x,y]
也就是gold最小值,如果当前的递归的值大于此值,那么就可以直接滚蛋了,就不用再递归下去了。
这样可以大大节省时间
ok,相当完美,过了。也不是啥难题,就是dfs的优化!!!!

#include<stdio.h>
typedef struct
{
	int dx,dy;
}a;
a move[4]={{-1,0},{1,0},{0,-1},{0,1}};//上下左右 
int maze[102][102]={0},tag[102][102]={0},mingold[102][102],m=0,n=0,x,y,color,find=0;
void dfs(int x,int y,int gold,int magic);
int main()
{
	//读取
	scanf("%d %d",&m,&n);
	//涂色 
	for(int i=1;i<=n;i++)
	{
		scanf("%d %d %d",&x,&y,&color);
		if(color==0)
		{
			maze[x][y]=2;//2为红色 
		}
		else
		{
			maze[x][y]=1;//黄色 
		}
	}
	//加城墙
	for(int i=0;i<=m+1;i++)
	{
		maze[0][i]=3;
		maze[i][0]=3;
		maze[m+1][i]=3;
		maze[i][m+1]=3;//3当城墙 
	}
	//预处理mingold,将所有元素都调成最大值
	for(int i=0;i<=m+1;i++)
	{
		for(int j=0;j<=m+1;j++)
		{
			mingold[i][j]=0xffff;
		}
	}
	dfs(1,1,0,1);
	if(find)
	{
		printf("%d",mingold[m][m]);
	}
	else
	{
		printf("-1");
	}
}

void dfs(int x,int y,int gold,int magic)
{
	tag[x][y]=1;
	//利用mingold减少递归次数
	if(gold<mingold[x][y])
	{
		mingold[x][y]=gold;//更新最小值
		if(x==m&&y==m)
		{
			find=1;
			tag[x][y]=0;
			return ;
		}
	}
	else
	{
		tag[x][y]=0;
		return ;//如果不是最小的,那么我还有什么意义去走你呢? 
	}
	int nx,ny; 
	for(int i=0;i<4;i++)//遍历四周
	{
		nx=x+move[i].dx;
		ny=y+move[i].dy;
		if(tag[nx][ny]==1||maze[nx][ny]==3)
		{
			continue;
		}
		else if(maze[nx][ny]==0)
		{
			if(magic)//如果可以使用魔法
			{
				maze[nx][ny]=maze[x][y];
//				printf("x=%d,y=%d,gold=%d\n",nx,ny,gold);
				dfs(nx,ny,gold+2,0);
				maze[nx][ny]=0;
			}
		}
		else if(maze[nx][ny]==2)
		{
			if(maze[x][y]==2)
			{
//				printf("x=%d,y=%d,gold=%d\n",nx,ny,gold);
				dfs(nx,ny,gold,1);
			}
			else if(maze[x][y]==1)
			{
//				printf("x=%d,y=%d,gold=%d\n",nx,ny,gold);
				dfs(nx,ny,gold+1,1);
			}
		}
		else if(maze[nx][ny]==1)
		{
			if(maze[x][y]==1)
			{
//				printf("x=%d,y=%d,gold=%d\n",nx,ny,gold);
				dfs(nx,ny,gold,1);
			}
			else if(maze[x][y]==2)
			{
//				printf("x=%d,y=%d,gold=%d\n",nx,ny,gold);
				dfs(nx,ny,gold+1,1);
			}
		}
	}
	tag[x][y]=0;
	return ;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值