TC-鸭子晨跑传说-题解

Solution

题意

本蒟蒻自认为形式化已经比较直接了,这里就只复制一遍了。

形式化地:给您一个 \(n \times m\) 的点阵,每个点有一个值,两个点之间有边当且仅当它们相邻且点值相同,试判断图上是否有环存在。

很显然,本题就是一个无向图判环,总共就 \(n \times m \le 2500\) 个点,相信您有一千种方法可以爆杀本题。本题解限于篇幅,只介绍一种方式,对于其他一些方法会简单提及,不详细展开。

下文中,设 \(N=n \times m\)

分析

题意很裸,我们不用多分析。

考虑直接尝试从每个格子开始搜,用一个 \(vis\) 记录每个格子是否到过,在不允许直接走回头路的前提下,如果走到一个走过的格子上直接返回。思路很简单了,其他的就是一些 DFS 自己的细节了。

注意,每次重新选点时不用清空 \(vis\),因为一次标记的 \(vis\) 实质上在同一连通块内,是本质相同的,因此每个点只需搜至多一遍(虽然 \(vis\) 清空也能过就是了,这是 Tenil 因上一道题出太难被换掉而产生的仁慈)。

时间复杂度 \(O(N)\)。(如果每次 \(vis\) 置空是 \(O(N^2)\) 的)

还可以维护一个并查集,每次搜索时加入当前边的两个端点,若当前边的两个端点都在并查集内,则有环。\(O(N \alpha(N))\)

实现

对每组数据:

  1. 清空,读入标记;
  2. 尝试从每个点开搜,dfs(x,y,fx,fy)(当前位置,上一步位置),边转移边标记,标记重合就 return。

Code

#include <iostream>
#include <cstdio>
#include <cctype>
#include <cstring>

int fr() {
	int x=0,f=1;char c=getchar();
	while(!isdigit(c)) {
		if(c=='-') f=-1;
		c=getchar();
	}
	while(isdigit(c)) {
		x=(x<<3)+(x<<1)+(c^48);
		c=getchar();
	}
	return x*f;
}

const int maxn=64;

int n,m,k;

char arr[maxn][maxn];
bool vis[maxn][maxn];
bool flag = false;

const int dx[]={1,0,-1,0};
const int dy[]={0,1,0,-1};

bool dfs(int i,int j,int pi,int pj)
{
	if(vis[i][j]) return true;
	vis[i][j]=true;
	for(int k = 0; k < 4; k++) {
		int nxtx = i+dx[k];
		int nxty = j+dy[k];
		if(nxtx>=0&&nxtx<n&&nxty>=0&&nxty<m&&arr[i][j]==arr[nxtx][nxty]) {
			if(!(nxtx==pi&&nxty==pj)) if(dfs(nxtx,nxty,i,j)) return true;
		}
	}
	return false;
}

int main() {
	int T=fr();
	while(T--) {
		memset(arr,0,sizeof(arr));
		memset(vis,0,sizeof(vis));
		n=fr(),m=fr();
		flag=false;
		for(int i = 0; i < n; i++) scanf("%s", arr[i]);
		for(int i = 0; i < n; i++) {
			for(int j = 0; j < m; j++) {
				if(vis[i][j]) continue;
				if(dfs(i,j,-1,-1)) {
					printf("Yes\n");
					flag=true;
				}
				if(flag) break;
			}
			if(flag) break;
		}
		if(flag) continue;
		printf("No\n");
	}
	return 0;
}

闲话

本题不卡输入输出,不卡 int,不卡 memset,留 std 不知道多少倍时间、空间,不卡性质,实乃业界良心,还望周知。

posted @ 2025-06-24 14:01  Tenil  阅读(10)  评论(0)    收藏  举报