题意:在一个n*n(n7)网格中,有些交叉点上有数字。你的任务是给每个格子画一条斜线(“/”和“\”),使得每个交叉点的数字等于和他们相连的斜线条数,且这些斜线不会构成环。
分析:方法是dfs,交叉点数字解决方法是以放入“\”和“/”为搜索对象,方格的端的有数字就减一,没数字就剪枝,其中有很多剪枝,比如每当搜索完一个方格后,方格的左端点如果是数字,那么数字应该为0,依次类推,方格处于右端,左上应该为0等等。其中关键难点是判断是否有环。看一位大佬的解法,以最新放进去的方格内容为对象,搜索方格四周,当点重复被搜索时,说明存在环。
bool loop(int r, int c, int f) { //判断是否有环
if (vis[r][c] == mark) return true;
vis[r][c] = mark; //每次都给他赋值,这个mark没有实际意义,只是避免和上次搜索重复
for (int i = 0; i < 4; i++) {
if ((i ^ 1) == f) continue; //i的相反方向是否等于f
int fr = r + dr[i], fc = c + dc[i]; //搜索四周
if (edge[r][c][fr][fc] == 0) continue; //判断是否有边
if (loop(fr, fc, i)) return true;
}
return false;
}
写的很简单,但非常实用!
#include <cstdio>
#include <cstring>
#include <cctype>
const int UP = 7 + 5;
const int dr[4] = { -1, 1, -1, 1 }; //左上,右下,右上,左下
const int dc[4] = { -1, 1, 1, -1 };
const int udr[2] = { 0, 0 }; //反斜杠和斜杠的上方坐标
const int udc[2] = { 0, 1 };
const int ddr[2] = { 1, 1 }; //反斜杠和斜杠的下方坐标
const int ddc[2] = { 1, 0 };
int n, N, finish, mark, vis[UP][UP];
char grid[UP][UP], ans[UP][UP], edge[UP][UP][UP][UP];
//ans[r][c] 与 grid[r][c], grid[r][c+1], grid[r+1][c], grid[r+1][c+1] 相关联
//r与c的下标从1开始
void renew(int r1, int c1, int r2, int c2) { //恢复状态
if (isdigit(grid[r1][c1])) grid[r1][c1]++;
if (isdigit(grid[r2][c2])) grid[r2][c2]++;
}
bool loop(int r, int c, int f) { //判断是否有环
if (vis[r][c] == mark) return true;
vis[r][c] = mark; //每次都给他赋值,这个mark没有实际意义,只是避免和上次搜索重复
for (int i = 0; i < 4; i++) {
if ((i ^ 1) == f) continue; //i的相反方向是否等于f
int fr = r + dr[i], fc = c + dc[i]; //搜索四周
if (edge[r][c][fr][fc] == 0) continue; //判断是否有边
if (loop(fr, fc, i)) return true;
}
return false;
}
bool dfs(int id) {
if (id == finish) return true;
if (id % N == 0) return dfs(id + 1); //该位置不做考虑,只是下一个位置的过渡
int r = id / N, c = id % N;
int jr = r + udr[0], jc = c + udc[0]; //判断该位置的数字是否大于0所用
for (int i = 0; i < 2; i++) {
int ufr = r + udr[i], ufc = c + udc[i];
int dfr = r + ddr[i], dfc = c + ddc[i];
if (isdigit(grid[ufr][ufc]) && grid[ufr][ufc] - 1 < '0') continue;
if (isdigit(grid[dfr][dfc]) && grid[dfr][dfc] - 1 < '0') continue;
if (isdigit(grid[ufr][ufc])) grid[ufr][ufc]--;
if (isdigit(grid[dfr][dfc])) grid[dfr][dfc]--;
if (grid[jr][jc] > '0') { //剪枝1
renew(ufr, ufc, dfr, dfc);
continue;
}
if (r == n) { //剪枝2
int sr = r + ddr[1], sc = c + ddc[1];
if (grid[sr][sc] > '0') {
renew(ufr, ufc, dfr, dfc);
continue;
}
}
if (c == n) { //剪枝3
int sr = r + udr[1], sc = c + udc[1];
if (grid[sr][sc] > '0') {
renew(ufr, ufc, dfr, dfc);
continue;
}
}
if (r == n && c == n) { //剪枝4
int sr = r + ddr[0], sc = c + ddc[0];
if (grid[sr][sc] > '0') {
renew(ufr, ufc, dfr, dfc);
continue;
}
}
ans[r][c] = i;
edge[ufr][ufc][dfr][dfc] = edge[dfr][dfc][ufr][ufc] = 1;
mark++;
if (loop(dfr, dfc, -1)) {
edge[ufr][ufc][dfr][dfc] = edge[dfr][dfc][ufr][ufc] = 0;
renew(ufr, ufc, dfr, dfc);
continue;
}
if (dfs(id + 1)) return true;
edge[ufr][ufc][dfr][dfc] = edge[dfr][dfc][ufr][ufc] = 0;
renew(ufr, ufc, dfr, dfc);
}
return false;
}
int main() {
int T;
scanf("%d", &T);
while (T--) {
scanf("%d", &n);
N = n + 1;
for (int r = 1; r <= N; r++) scanf("%s", grid[r] + 1);
memset(vis, 0, sizeof(vis));
memset(edge, 0, sizeof(edge));
mark = 1;
finish = N * N;
dfs(N + 1);
for (int r = 1; r <= n; r++) {
for (int c = 1; c <= n; c++) {
if (ans[r][c] == 0) printf("\\");
else printf("/");
}
printf("\n");
}
}
return 0;
}