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))\);
实现
对每组数据:
- 清空,读入标记;
- 尝试从每个点开搜,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 不知道多少倍时间、空间,不卡性质,实乃业界良心,还望周知。

浙公网安备 33010602011771号