【棋盘问题】-LETTERS

信息学奥赛一本通(C++版)在线评测系统


此部分开始搜索专题的棋盘问题,棋盘问题多数和方向数组,标记数组,回溯算法,多支深搜等有关,需要找到所有解,然后得出最优解。


解题思路:

(1)要求访问过字母的路线,并且使得字母的不同数量为最大,那么可以从出发点开始,遍历所有的路线,把每条路线的答案都保存下来,最后取最优解。

(2)在路线中,如何让路线按照自己的规则流动起来呢?这里面有两个限定条件,其一为下一步的路线不能是已经走过的位置,其二为下一步的字母不能是已经走过的字母,那么我们可以设一个Bool的标记数组,来记录已经走过的位置,设一个Bool的vis数组,标记已经走过的字母

(3)所以可行性方案应该是当此时的位置没有被访问过,并且该字母也没有访问过的话,可以走到这里,然后步数加1,既然走到这了,那么意味着,此刻的位置需要打上标记,该位置的字母也需要打上标记

(4)走完这一步,接下来往哪里走呢?可以有4个选择,上下左右,所以可以设置一个方向数组来表示下一步的偏移量,很明显,在地图中,是不能越界的,所以即使有4种选择,必须判断是否越界,没有越界的话,才能走到下一步。  

(5)如果走到终点后,发现都已经不能走了,那么应该开始回头了,联系全排列问题,回溯算法也就是恢复现场的步骤,当离开这个位置返回到上一步的时候,该位置应该取消掉标记,并且步数-1

 (6)每一步位置的更新都会带来步数的变化,也就是可行解的增加,如何取最优解呢?只需要在函数的开头做一个大小判断即可,取最大值。

(7)这时候就会有人有疑问,递归函数明明都是有出口的,有return的,这里怎么没有呢?首先void函数是一个无返回值的操作函数,有无return均可,这道题的出口在哪里?出口在于把左右的路线都走完,也就是for循环都执行完,找到了所有的路线,便会退出。

如下代码是按照上,下,左,右的顺序依次判断是否会越界,是否可以走。

  1. 第一个位置(1,1)可以走,用掉H
  2. 第二个位置,从第一步往上走(越界),往下走(可以),用掉A
  3. 第三个位置,从第二步往上走(A已经用过),往下走(可以),用掉D
  4. 第四个位置,从第三步往上走(D已经用过),往下走(越界),往左走(越界),往右走,用G
  5. 第五个位置,从第四步往上走(可以),用掉J
  6. 第六个位置,从第五步往上走(可以),用掉F
  7. 第七个位置,从第六步往上走(越界),往下走(J用过),往左走(H用过),往右走(D用过)
  8. 此时已经无路可走,那么F取消掉标记,步数减1,从第五个位置开始继续,因为上已经走过,往下走,下面(J用过),往左走(A用过),往右走(H用过),那么再次回退到第四个位置,依旧按照for循环规定的方向来走,但是每层递归函数遍历的i的值是不一样的。

(8)到这里,应该读懂了此递归函数到底是如何出去的。(完结撒花)


#include<bits/stdc++.h>
using namespace std;

int r,s,ans,step;//ans表示最优解 ,step表示可行解 
char a[30][30];//表示字符的二维数组 
bool vis[30];//表示标记已经走过的字母
bool aa[30][30];//表示标记已经走过的位置
int hang[5]={-1,1,0,0};//表示行的偏移量数组 
int lie[5]={0,0,-1,1};//表示列的偏移量数组

void dfs(int x,int y)
{
	if(ans<step)//如果当前的步数大于最大值 
	ans=step;//最大值更新 
	
	if(aa[x][y]==0&&vis[a[x][y]-64]==0)//如果该位置没有访问过并且该字母也没有访问过
	{
		step++;//步数+1
		vis[a[x][y]-64]=1;//标记该字母已经走过
		aa[x][y]=1;//标记该位置已经访问过 
		
		for(int i=0;i<=3;i++)
		{
			int xx=x+hang[i];//行号更新 
			int yy=y+lie[i];//列号更新
			
			if(xx>r||xx<1||yy>s||yy<1)//如果位置越界
			continue;//结束当前循环 
			
			dfs(xx,yy); //继续深搜 
		}
		
		vis[a[x][y]-64]=0;//回溯,标记该字母取消 
		aa[x][y]=0;//回溯,标记该位置取消 
		step--;//步数-1 
	}
	
}

int main()
{
	cin>>r>>s;
	
	for(int i=1;i<=r;i++)
	for(int j=1;j<=s;j++)
	cin>>a[i][j];//输入字符二维数组
	
	dfs(1,1);//表示从左上角位置开始走 
	cout<<ans; 
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值