UVA - 11694 Gokigen Naname(dfs)

题意:在一个n*n(n\leqslant7)网格中,有些交叉点上有数字。你的任务是给每个格子画一条斜线(“/”和“\”),使得每个交叉点的数字等于和他们相连的斜线条数,且这些斜线不会构成环。

分析:方法是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;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值