51nod1416两点(dfs或并查集)

福克斯在玩一款手机解迷游戏,这个游戏叫做”两点”。基础级别的时候是在一个n×m单元上玩的。像这样:




 

每一个单元有包含一个有色点。我们将用不同的大写字母来表示不同的颜色。

这个游戏的关键是要找出一个包含同一颜色的环。看上图中4个蓝点,形成了一个环。一般的,我们将一个序列 d1,d2,...,dk 看成一个环,当且仅当它符合下列条件时:

1.    这k个点不一样,即当 i≠j时, di 和 dj不同。

2.    k至少是4。

3.    所有的点是同一种颜色。

4.    对于所有的 1≤i≤k-1: di 和 di+1 是相邻的。还有 dk 和 d1 也应该相邻。单元 x 和单元 y 是相邻的当且仅当他们有公共边。

当给出一幅格点时,请确定里面是否有环。

Input
单组测试数据。
第一行包含两个整数n和m (2≤n,m≤50):板子的行和列。
接下来n行,每行包含一个有m个字母的串,表示当前行每一个点的颜色。每一个字母都是大写字母。
Output
如果有环输出Yes,否则输出No。
Input示例
3 4
AAAA
ABCA
AAAA
3 4
AAAA
ABCA
AADA
Output示例
Yes
No

1,dfs的思路就是暴力搜索,不往回搜(这步很重要),如果搜索的点标记过则有环,输出Yes,否则输出No
代码:
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
char s[55][55];
int visit[55][55];
int dir[4][2]={-1,0, 1,0, 0,-1, 0,1};
int n,m,flag;
void dfs(int x,int y,int sum,char ch)//x,y为坐标,ch是要搜索的字符,sum是搜索方向,0是上,1是下,2是左,3是右 
{
    if(x<0||x>n||y<0||y>m||ch!=s[x][y])//越界和不符合条件 
        return ;
    if(visit[x][y]==1)//搜索终点,找到之前搜索过的 
    {
        flag=1;
        return ;
    }
    if(flag)
        return;
    visit[x][y]=1;
    for(int i=0;i<4;i++)
    {
        if(sum==0&&i==1)    continue;//如果是从向上搜索过来的,就不往下搜索 
        if(sum==1&&i==0)    continue;//往下搜索过来的,不往上搜索 
        if(sum==2&&i==3)    continue;//往左搜索过来的,不往右搜索
        if(sum==3&&i==2)    continue;//往右搜索过来的,不往左搜索
        int dx=x+dir[i][0];
        int dy=y+dir[i][1];
        dfs(dx,dy,i,ch);
        
    }
}
int main()
{
    while(cin>>n>>m)
    {
        flag=0;
        memset(visit,0,sizeof(visit));
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
                cin>>s[i][j];
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
            {
                if(!visit[i][j])//搜索每一个字母 
                    dfs(i,j,-1,s[i][j]);
            }
        if(flag==1)
            cout<<"Yes"<<endl;
        else
            cout<<"No"<<endl;
    }
    return 0;
} 

2.并查集思路:往下,往右走,如果字母相同,则令成同一祖先。如果有环,就成了矩形,矩形是关于对角线对称的,往下的会使最后的顶点先变成祖先相等,之后往右则和最后顶点祖先相等,则标记,退出循环就好。

代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
int fa[3000];
char s[60][60];
int find(int x)//查询祖先 
{
    if(fa[x]==x)
        return x;
    else
        return fa[x]=find(fa[x]);
}
bool unionn(int x,int y)//连通祖先 
{
    x=find(x);
    y=find(y);
    if(x==y)
        return true;//祖先相等则返回正确 
    fa[x]=y;
    return false;
}
int main()
{
    int n,m;
    while(cin>>n>>m)
    {
        bool flag=false;
        for(int i=0;i<=n*m;i++)//每个点按矩阵编号1到n*m 
            fa[i]=i;
        for(int i=0;i<n;i++)
            for(int j=0;j<m;j++)
                cin>>s[i][j];
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<m;j++)
            {
                if(i!=n-1&&s[i][j]==s[i+1][j]&&unionn(i*m+j,(i+1)*m+j))//往下走 
                    flag=!flag;
                if(j!=m-1&&s[i][j]==s[i][j+1]&&unionn(i*m+j,i*m+j+1))//往右走,碰到祖先相等则有环 
                    flag=!flag;
                if(flag)
                    break;
            }
            if(flag)
                break;
        }
        if(flag)
            cout<<"Yes"<<endl;
        else
            cout<<"No"<<endl;
    } 
    return 0;
} 
原文地址:https://www.cnblogs.com/xiongtao/p/9290328.html