题目:
当N=3使,一种可能的路径如下:
普通的dfs回溯当然是会被卡的。
思路:对于一条合法的路径,除出发点和目标格子外,每一个中间格子都必然有“一进一出”的过程。所以在搜索过程中,必须保证每个尚未经过的格子都与至少两个尚未经过的格子相邻(除非当时Betsy就在它旁边)。
用一个数组last
记录每个格子周围没访问过的格子数量,每次进入和退出一个格子的时候,都会更新他周围未访问格点的last
数组。
当四周有一个格子last
值为 1,则必须走这个格子。注意为了方便统一处理,终点的last
值要增大 1。
代码如下:
import java.util.Scanner;
public class _Betsy的旅行 {
static int n, ans;
static int[] dx = {0, 0, 1, -1};//方向数组
static int[] dy = {1, -1, 0, 0};
static boolean[][] vis = new boolean[9][9];
static int[][] last = new int[9][9];//数组last记录每个格子周围没访问过的格子数量
static boolean in(int x, int y) {//判断越没越界
return x >= 1 && x <= n && y >= 1 && y <= n;
}
static void dfs(int x, int y, int cnt) {
if (x == n && y == 1) {//如果到了左下角的点
if (cnt == n * n)//访问了全部的点
ans++;
return;
}
int dir = -1;//定义将要访问的方向
vis[x][y] = true;//标记已访问
for (int i = 0; i < 4; i++) {
int ex = x + dx[i];//下一个要访问的点
int ey = y + dy[i];
if (in(ex, ey) && !vis[ex][ey]) {
last[ex][ey]--;
if (last[ex][ey] == 1) {//如果只剩下一个出口,只能访问这个
dir = i;
}
}
}
for (int i = 0; i < 4; i++) {
if (dir != -1 && dir != i) continue;
int tx = x + dx[i];
int ty = y + dy[i];
if (in(tx, ty) && !vis[tx][ty]) {
boolean is_go = true;
int r = 0;
for (int j = 0; j < 4; j++) {
int ex = tx + dx[j];
int ey = ty + dy[j];
if (in(ex, ey) && !vis[ex][ey]) {
if (last[ex][ey] < 2) {//保证有2个,一进一出
is_go = false;
break;
} else if (last[ex][ey] == 2) {
r++;
}
}
}
if (is_go && r <= 1) {
dfs(tx, ty, cnt + 1);
}
}
}
vis[x][y] = false;
for (int i = 0; i < 4; i++) {
int tx = x + dx[i];
int ty = y + dy[i];
if (in(tx, ty) && !vis[tx][ty]) {
last[tx][ty]++;
}
}
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= n; ++j) {
for (int k = 0; k < 4; ++k) {
int tx = i + dx[k];
int ty = j + dy[k];
if (in(tx, ty))
++last[i][j];
}
}
}
last[n][1]++;
ans=0;
dfs(1, 1, 1);
System.out.println(ans);
}
}