1. 程式人生 > >無向圖同構 (雜湊)

無向圖同構 (雜湊)

題目

Problem Description

如果一個無向圖重標號後與另一個無向圖完全一致(即對於任意兩點,他們之間的邊在兩個圖中都存在或都不存在),則稱兩個無向圖同構。

給定兩個n個點m條邊的無向圖,判定兩個無向圖是否同構。

Input

第一行一個數T,表示有T組資料(T<=20)
對於每一組資料:
第一行兩個數n,m,表示要判定的兩個無向圖都是n個點m條邊(n<=200,m<=4000)
接下來m行,每行兩個數x,y,表示在第一個圖中存在一條邊連線點x和點y(1<=x,y<=n)
接下來m行,每行兩個數x,y,表示在第二個圖中存在一條邊連線點x和點y(1<=x,y<=n)

Output

對於每一組資料,如果兩個無向圖同構則輸出”YES”,否則輸出”NO”

Sample Input

4
5 6
1 2
1 3
1 5
4 1
2 3
5 4
3 1
5 4
3 4
2 3
3 5
1 2

5 6
1 2
1 3
1 5
4 1
2 3
5 4
3 1
5 4
3 4
2 3
3 5
1 4

3 2
1 2
2 3
2 1
1 3

4 3
1 2
2 3
3 4
1 2
1 3
1 4

Sample Output

YES
NO
YES
NO

分析

  • 看兩個圖是否同構,可以想到用雜湊值來確定一個圖。
  • 那麼對於一個圖,它的雜湊值就應該只和它的結構有關,和點的編號無關,於是可以有下面這種雜湊規則
    • 對於每個點,它的雜湊值是它的權值與“和它相鄰的點”的權值的和(當然可以再乘一些數再模一下)。
    • 起初全部點的權值都為 1,然後進行多次改變操作,每次中先把點按上一次的權值排個序,再按上面的規則修改雜湊值即可。
    • 由於排了序,那麼此圖的雜湊值就只會和它的結構有關了。
  • 進行多次改變後,若兩個圖中的點,權值情況相同,那麼我們就可以看成它們同構了。(當然運氣不好可能可以被卡,那就多變換幾次,雜湊操作也弄複雜一點吧)

程式

#include <cstdio>
#include <algorithm>
#define For(G,x) for(int h=G.head[x],o=G.V[h]; h; o=G.V[h=G.to[h]])
#define N 205
#define M 4005
using namespace std;
struct Tu{
    int head[N],to[M*2],V[M*2],num;
    void Add(int x,int y){to[++num]=head[x],head[x]=num,V[num]=y;}
}A,B;
struct zzk{int id,v;} a[2][N],b[2][N];
int n,m,cnt,k,F=1,f1[2][N],f2[2][N];
bool cmp(zzk x,zzk y){return x.v<y.v;}

void Hash(){
    sort(a[cnt]+1,a[cnt]+n+1,cmp);
    sort(b[cnt]+1,b[cnt]+n+1,cmp);
    for (int i=1,u=a[cnt][i].id; i<=n; i++,u=a[cnt][i].id){
        k=f1[cnt][u]*18;        //隨意弄個規則修改 Hash 值 
        For(A,u) k+=f1[cnt][o]*666233;
        a[cnt^1][i].id=u;
        f1[cnt^1][u]=a[cnt^1][i].v=k;
    }
    for (int i=1,u=b[cnt][i].id; i<=n; i++,u=b[cnt][i].id){
        k=f2[cnt][u]*18;        //用同一個規則修改 Hash 值 
        For(B,u) k+=f2[cnt][o]*666233;
        b[cnt^1][i].id=u;
        f2[cnt^1][u]=b[cnt^1][i].v=k;
    }
    cnt^=1;
}

int main(){
    for (int T=(scanf("%d",&T),T),uu,vv; T--; F=1){

        A.num=B.num=0;
        for (int i=1; i<=n; i++) A.head[i]=B.head[i]=0;

        scanf("%d%d",&n,&m);
        for (int i=1; i<=m; i++) scanf("%d%d",&uu,&vv),A.Add(uu,vv),A.Add(vv,uu);
        for (int i=1; i<=m; i++) scanf("%d%d",&uu,&vv),B.Add(uu,vv),B.Add(vv,uu);
        for (int i=1; i<=n; i++){
            a[0][i].id=a[1][i].id=b[0][i].id=b[1][i].id=i;
            f1[cnt][i]=a[cnt][i].v=f2[cnt][i]=b[cnt][i].v=1;
        }
        for (int i=1; i<=n; i++) Hash();    //多次變換 
        sort(a[cnt]+1,a[cnt]+n+1,cmp);
        sort(b[cnt]+1,b[cnt]+n+1,cmp);
        for (int i=1; i<=n; i++)
            if (a[cnt][i].v!=b[cnt][i].v) {F=0; break;}
        puts(F?"YES":"NO");
    }
}