老曹骑士
题目大意
给你一个网格,然后有一些点要走,问你从一个要走的出发走过所有要走的点在回到你出发的点。
要用的最小步数,而且每次走是像象棋的马一样走“日”字形。
思路
你看到范围,不难想到其实可以直接 bfs。
状态就是
f
x
,
y
,
s
,
z
f_{x,y,s,z}
fx,y,s,z 表示当前走到
(
x
,
y
)
(x,y)
(x,y) 的位置,你是从第
s
s
s 个要走的点出发的,然后当前要走的点你走的状态时
z
z
z。(第
i
i
i 位表示第
i
i
i 个你要走的点走或没走)
然后就正常跑 bfs,如果当前的状态
(
x
,
y
)
(x,y)
(x,y) 就是第
s
s
s 个要走的点而且
z
=
2
k
−
1
z=2^k-1
z=2k−1(即所有要走的点都走到了),那就可以作为结束的状态了。
然后就可以啦。
代码
#include<queue>
#include<cstdio>
#include<cstring>
#define ll long long
using namespace std;
int n, k, xx[11], yy[11], x, y, s, z;
int f[21][21][10][1024], ans;
int dx[8] = {-1, -2, -2, -1, 1, 2, 2, 1};//日字形
int dy[8] = {-2, -1, 1, 2, 2, 1, -1, -2};
int hx[21][21];
bool go[21][21][10][1024];
struct zt {
int x, y, s, z;
};
queue <zt> q;
bool in(int x, int y) {
if (x < 1 || x > n) return 0;
if (y < 1 || y > n) return 0;
return 1;
}
int main() {
memset(f, 0x7f, sizeof(f));
scanf("%d %d", &n, &k);
for (int i = 0; i < k; i++) {
scanf("%d %d", &xx[i], &yy[i]);
f[xx[i]][yy[i]][i][1 << i] = 0;
q.push((zt){xx[i], yy[i], i, (1 << i)});
hx[xx[i]][yy[i]] = i + 1;
}
ans = 2e9;
while (!q.empty()) {
x = q.front().x; y = q.front().y; s = q.front().s; z = q.front().z;
q.pop();
if (go[x][y][s][z]) continue;
go[x][y][s][z] = 1;
if (x == xx[s] && y == yy[s] && z == (1 << k) - 1) {//走完了
ans = min(ans, f[x][y][s][z]);
continue;
}
for (int i = 0; i < 8; i++) {
int tx = x + dx[i], ty = y + dy[i];
if (!in(tx, ty)) continue;
int tz = z;
if (hx[tx][ty]) {//走到一个要走的点
tz |= (1 << (hx[tx][ty] - 1));
}
if (f[x][y][s][z] + 1 < f[tx][ty][s][tz]) {
f[tx][ty][s][tz] = f[x][y][s][z] + 1;
q.push((zt){tx, ty, s, tz});
}
}
}
printf("%d", ans);
return 0;
}