2488:A Knight's Journey:马步遍历,简单而巧妙地解决字典序问题

题目大意

在这里插入图片描述
骑士厌倦了一次又一次看到相同的黑白方块,并决定踏上旅途

世界各地。 每当骑士移动时,它在一个方向上是两个正方形,而垂直于该方向的正方形是一个正方形。 骑士的世界就是他赖以生存的棋盘。 我们的骑士生活在棋盘上,棋盘的面积比普通的8 * 8棋盘小,但它仍然是矩形的。 您可以帮助这个冒险的骑士制定旅行计划吗?

问题

找到一条让骑士访问每个广场一次的路径。 骑士可以在棋盘的任何正方形上开始和结束。

思路分析

首先要走完全程我们显然可以搜索,这道题普通的dfs就足够了,然而有一个地方比较棘手,就是要按字典序输出结果。怎么做呢?

  • 因为我们起点不确定,因此从上到下,从左到右遍历起始点,这样可以保证第一个点总是字典序最小的起始点/
  • 对一次搜索内部,dx[8] = { -2,-2,-1,-1,1,1,2,2 }, dy[8] = { -1,1,-2,2,-2,2,-1,1 };,仔细看这八个策略,我们是从上到小,从左到右的次序,也就是说第一个合格的策略总是字典序最小的。
  • 然后就要注意dfs的回溯了,我们在函数的开始将当前位置加入路径,如果找不到一个到重点的方案,记着重新把路径返回前一状态。
#include<iostream>
#include<string.h>
#include<string>
using namespace std;

#define MAX 30
#define ll int

// x从上到下,y从左到右进行变化,就可以满足字典序
ll dx[8] = { -2,-2,-1,-1,1,1,2,2 }, dy[8] = { -1,1,-2,2,-2,2,-1,1 };
ll w, l, vis[MAX][MAX];
string s[MAX][MAX], step;

bool check(ll x, ll y) {
	if (x <= 0 || x > l || y <= 0 || y > w || vis[x][y])return false;
	return true;
}

// 当前点的坐标以及已经遍历的点的数目
ll dfs(ll x, ll y, ll cnt) {
	step += s[x][y];
	vis[x][y] = 1;
	// 结束条件: 走完全部的点
	if (cnt + 1 == l * w) return true;
	for (int i = 0; i < 8; i++) {
		ll xx = x + dx[i], yy = y + dy[i];
		if (check(xx, yy)) {
			if (dfs(xx, yy, cnt + 1)) return true;
			vis[xx][yy] = 0; // 跑完要回溯
		}
	}
	//如果你找不到路就要把step还原
	step.erase(step.size() - 2, 2);
	return false;
}

int main() {
	int m; cin >> m;
	//把每个位置对应的字符存储起来
	for (int i = 1; i <= 26; i++) {
		for (int j = 1; j <= 26; j++) {
			s[i][j] = (i - 1 + 'A');
			if (j >= 10) s[i][j] += j / 10 + '0';
			s[i][j] += j % 10 + '0';
		}
	}
	for (int i = 1; i <= m; i++) {
		printf("Scenario #%d:\n", i);
		cin >> w >> l; //w行 l列
		ll sign = 0;
		for (int j = 1; j <= l && !sign; j++) {
			for (int k = 1; k <= w && !sign; k++) {
				step.clear();
				memset(vis, 0, sizeof(vis));
				if (dfs(j, k, 0)) sign = 1;
			}
		}
		if (sign) cout << step << endl;
		else cout << "impossible" << endl;
		cout << endl;
	}
}
发布了211 篇原创文章 · 获赞 14 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/csyifanZhang/article/details/105331838
今日推荐