P1141 01迷宫

首先是读入上的问题,这里可以使用二维字符数组存储这张图,但也可以使用

scanf("%1d",&maze[i][j]);

人为规定读入数据长度

 

从整体方法看,很明显的搜索问题,使用BFS和DFS都可以,但还是DFS好写

但实际上手后发现三个点超时,想了想是由于大量重复计算导致的

然后就发现实际上这题是一个联通块问题,所有联通的的格子的答案是相同的

于是需要一个方式来维护连通格子的答案

首先想到的是记忆化搜索,每个格子只执行一边操作,核心代码如图

void DFS(int i, int j, int marker ,int ll){  //i,j为坐标,marker记录从1还是0格子来的,ll记忆化
	if(maze[i][j] == -1 || vis[i][j] != -1 || !(marker ^ maze[i][j])) return ; //分别是 边界(通过memeset使图周围一圈为-1) 重复情况 是否为0到1或1到0
	vis[i][j] = ll;  //将本次联通的格子全部指向ll
	ans[ll]++;
	DFS(i + 1, j, maze[i][j], ll);
	DFS(i, j + 1, maze[i][j], ll);
	DFS(i, j - 1, maze[i][j], ll);
	DFS(i - 1, j, maze[i][j], ll);
}

for(i = 1; i <= m; i++){
		cin >> x >> y;
		if(vis[x][y] == -1) DFS(x,y,1 ^ maze[x][y], i);
		else ans[i] = ans[vis[x][y]]; 
		cout << ans[i] << endl;
}

然后思索了一下,想到了并查集,和记忆化异曲同工,但在储存时不会用pair(恼火) ,借鉴了一位大佬的二维坐标转数字

即  因为( i , j < n )所以将 i * n + j 便可以使每个坐标都转换为独一无二的数字 

这个函数短小且需大量调用,所以使用内联函数inline来节省时间

inline int vti(int x, int y) return y * n + x;  

剩下就是细心写并查集了

int find(int k){
	if(f[k] == k) return k;
	else return f[k] = find(f[k]);
}
void bin(int i, int j){
	f[find(i)] = find(j);
}//并查集标准代码
for(int i = 1; i <= n; i++) {
	for(int j = 1; j <= n; j++){
		if(maze[i][j] + maze[i + 1][j] == 1) 
			bin(vti(i,j), vti(i+1,j));   
		if(maze[i][j] + maze[i][j + 1] == 1) 
			bin(vti(i,j), vti(i,j+1));	
	}  //逐个方块向右,向下扩展
}
for(i = 1; i <= n; i++)
	for(j = 1; j <= n; j++) 
		ans[find(vti(i,j))]++;  //记录联通块数量并储存在老大的ans中
for(i = 1; i <= m; i++){
	cin >> x >> y;
	cout << ans[find(vti(x,y))] << endl;
}

但既然谈到了联通块,我隐约回忆起tarjan算法,等我复习回来看看能不能再写一种解法

posted @   懵逼树  阅读(43)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示