Multiplayer Moo[ [ 并查集+dfs连通块 ] / [ dfs ] ]

题目链接

题目描述:

The cows have come up with a creative new game, surprisingly giving it the least creative name possible: "Moo".
The game of Moo is played on an N×N grid of square cells, where a cow claims a grid cell by yelling "moo!" and writing her numeric ID number in the cell.

At the end of the game, every cell contains a number. At this point, a cow wins the game if she has created a region of connected cells as least as large as any other region. A "region" is defined as a group of cells all with the same ID number, where every cell in the region is directly adjacent to some other cell in the same region either above, below, left, or to the right (diagonals don't count).

Since it is a bit boring to play as individuals, the cows are also interested in pairing up to play as teams. A team of two cows can create a region as before, but now the cells in the region can belong to either of the two cows on the team.

Given the final state of the game board, please help the cows compute the number of cells belonging to the largest region that any one cow owns, and the number of cells belonging to the largest region that can be claimed by a two-cow team. A region claimed by a two-cow team only counts if it contains the ID numbers of both cows on the team, not just one of the cows.


输入:

The first line of input contains N (1≤N≤250). The next N lines each contain N integers (each in the range 0…106), describing the final state of the game board. At least two distinct ID numbers will be present in the board.


输出

The first line of output should describe the largest region size claimed by any single cow, and the second line of output should describe the largest region size claimed by any team of two cows.


样例输入:

4
2 3 9 3
4 9 9 1
9 9 1 7
2 1 1 9

样例输出:

5
10

题意:

题目是英文的,我给大家讲一讲题意吧,它的意思好像黑社会进行拉帮结派,首先给你一个N,有N×N的方格,

这个方格里有数字,每一个数字代表一个势力。然后题目给了两个问题。

  1. 请问现在势力相邻    人数最大的值是多少???
  2. 请问如果给你两个势力联合,然后再问一下相邻的最大势力的人数为多少???

其实第一个问很简单,就是直接爆搜更新即可,但是第二问就很难了,因为涉及了两个势力结合。

方法有两种:

第一种:用dfs传进去的是两个变量,就是我们枚举的所有的势力。

第二种:利用把每一个分割开的势力变成一组又一组的并查集,然后进行建边,

最后就是历遍所有边且势力相等的。只要标记用得好,剪枝如剪纸。

贴上代码:(由Brother Ming 提供):

参考代码

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int vis[505][505];
int G[505][505];
int n,sum,ans1,ans2,maxz=-1;
typedef struct node{
    int No,sum;
    node (int no=-2,int s=0):No(no),sum(s){}
    bool operator <(const node &p)const {
        return sum>p.sum;
    }
}node;
node a[N];
int dir[4][2]={
        {-1,0},
    {0,-1} , {0,1},
        {1,0}
};
bool judge(int x,int y){
    if(x<=0||x>n||y<=0||y>n){
        return false;
    }
    return true;
}
void dfs(int x,int y,int n1,int n2=-1){//骚操作,巧用缺省值
     int tx,ty;
     for(int i=0;i<4;i++){
         tx=x+dir[i][0];
         ty=y+dir[i][1];
         if(judge(tx,ty)&&vis[tx][ty]==0&&(G[tx][ty]==n1||G[tx][ty]==n2)){
             vis[tx][ty]=1;
             sum++;
             dfs(tx,ty,n1,n2);
         }
     }
}
int solve(int n1,int n2){
    int t=1;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            if(vis[i][j]==0&&(G[i][j]==n1||G[i][j]==n2)){
                vis[i][j]=1;
                sum=1;
                dfs(i,j,n1,n2);
                t=max(t,sum);
            }
        }
    }
    return t;
}
void Q1(){
    ans1=0;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            if(vis[i][j]==0){
                vis[i][j]=1;
                sum=1;
                dfs(i,j,G[i][j]);
                ans1=max(ans1,sum);
                a[G[i][j]].No=G[i][j];
                a[G[i][j]].sum+=sum;
            }
        }
    }
    printf("%d\n",ans1);
}
void Q2(){
    sort(a,a+maxz+1);
    ans2=ans1;
    for(int i=0;i<=maxz;i++){
        for(int j=0;j<i;j++){

            if(a[i].sum+a[j].sum<=ans2) break;

            memset(vis,0,sizeof(vis));
            sum=1;
            ans2=max(ans2,solve(a[i].No,a[j].No));
        }
    }
    printf("%d\n",ans2);
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            scanf("%d",&G[i][j]),maxz=max(maxz,G[i][j]);
    //printf("%d\n",maxz);
    Q1();
    Q2();
    return 0;
}
/*
4
2 3 9 3
4 9 9 1
9 9 1 7
2 1 1 9
*/

第二种方法:(由Brother Dragon 提供) 

参考代码

由于Brother Dragon 提供的写法太骚了。所以一定要解释一波,不然不能领悟到其中的奥秘:

/*
    when i was young i listen to the radio
    if you ask me how much i love you,
    the moonlight stand for my heart
    you are very matter in my mind
*/
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
const int M=606;
int cnt,n,head[N],Moo[N<<1],G[N<<1],pre[N<<1],ans1,ans2;
int vt[N];              //标记点出现的时间
bool vis[N];            //标记边
int dir[2][2]={         //方向数组
            {0,1},      //右
      {1,0}             //下
};
typedef struct Edge{
    int to,next;
}Edge;
Edge edge[N];
void add_edge(int u,int v){
    edge[cnt]=Edge{v,head[u]};
    head[u]=cnt++;
}
int Fa(int x){return x==pre[x]?x:pre[x]=Fa(pre[x]);}
void Union(int x,int y){x=Fa(x);y=Fa(y);pre[x]=y;}
int getid(int x,int y){return (x-1)*n+y;}       // 二维输入放进一维数组
int dfs(int u,int tag,int n1,int n2){
    vt[u]=tag;
    int res=0;
    for(int i=head[u];~i;i=edge[i].next){
        int to=edge[i].to;
        if(vis[i]==true||vt[to]==tag||(G[to]!=n1&&G[to]!=n2)) continue;
        vis[i^1]=vis[i]=true;           //很骚的操作,反向边也给标记了
        res+=dfs(to,tag,n1,n2);
    }
    return res+Moo[u];
}
void Q1(){
     ans1=0;
     for(int u=1;u<=n*n;u++){
         ans1=max(ans1,++Moo[Fa(u)]);   //更新连通块的大小
     }
     printf("%d\n",ans1);
}
void Q2(){
    ans2=ans1;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            for(int k=0;k<2;k++){
                int x=i+dir[k][0];      //通过两个方向建边
                int y=j+dir[k][1];
                if(x>n||y>n||G[getid(x,y)]==G[getid(i,j)])continue;
                int u=Fa(getid(x,y));   //找到对应的祖先
                int v=Fa(getid(i,j));
                add_edge(u,v),add_edge(v,u);//建立关系
            }
        }
    }
    int tag=1;                      //时间戳
    for(int u=1;u<=n*n;u++){if(G[u]>0)  //首先看看是不是祖先
        for(int i=head[u];~i;i=edge[i].next){   //如果是祖先,那么历遍一下和它相连的边
            int to=edge[i].to;          //相邻的区域
            if(vis[i])continue;         //如果这个边被访问过了,就不要再访问了。
            int res=dfs(u,tag++,G[u],G[to]);//两个祖先的值传进去
            ans2=max(ans2,res);
        }
    }
    printf("%d\n",ans2);
}
int main(){
    scanf("%d",&n);
    memset(head,-1,sizeof(head));   //初始化
    for(int i=0;i<=n*n;i++){
        pre[i]=i;
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            scanf("%d",&G[getid(i,j)]);
            if(i>1&&G[getid(i,j)]==G[getid(i-1,j)]){//如果这个左边的值一样,合并
                Union(getid(i,j),getid(i-1,j));
            }
            if(j>1&&G[getid(i,j)]==G[getid(i,j-1)]){//如果和上边的值一样合并
                Union(getid(i,j),getid(i,j-1));
            }
        }
    }
    /*for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            printf("%d%c",pre[getid(i,j)],j==n?'\n':' ');
        }
    }*/
    Q1();
    Q2();
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Z_sea/article/details/81429002
dfs