无向图的图同构问题(Graph Isomorphism)。所谓图同构就是边和点的几何构型是否一致,不涉及点编号和边权值。
这道题大大简化了这个问题。在这道题中,点的最大度数是2
,所以图的构型中每个连通分量要么是链(包括单个点),要么是单环(单环就是指“单重环”,是对一个连通分量的描述;而“简单环”是对一条环路的描述)。
可以想到,每一个连通分量用两个变量来标识:一个是点数;一个区分是链还是环。
对此设定一种排序规则,然后对两个图比较是否全等就完事了。
我一直使用的都是并查集的“根记录个数(负值)版本”。
C++的pair
自带的operator<
函数,对基本类型按照字典序比较。
#include <iostream>
#include <algorithm>
#include <vector>
#include <cstring>
#include <utility>
using namespace std;
const int MAXN = 10001;
int N, M, T;
int pre[2][MAXN];
bool flag[2][MAXN]; // 仅对根节点生效,区分该连通分量是不是环
vector<pair<int, int>> v[2];
void init()
{
memset(pre, -1, sizeof pre);
memset(flag, 0, sizeof flag);
for (int i = 0; i < 2; i++)
v[i].clear();
}
int f(int x, int id)
{
if (pre[id][x] < 0) return x;
return pre[id][x] = f(pre[id][x], id);
}
bool u(int n1, int n2, int id)
{
int f1 = f(n1, id);
int f2 = f(n2, id);
if (f1 != f2)
{
if (pre[id][f1] <= pre[id][f2])
{
pre[id][f1] += pre[id][f2];
pre[id][f2] = f1;
}
else
{
pre[id][f2] += pre[id][f1];
pre[id][f1] = f2;
}
return true;
}
return false;
}
int main()
{
int a, b;
scanf("%d", &T);
for (int num = 1; T--; num++)
{
init();
for (int t = 0; t < 2; t++)
{
scanf("%d%d", &N, &M);
for (int i = 0; i < M; i++)
{
scanf("%d%d", &a, &b);
if (!u(a, b, t)) // 有环,这个连通分量已经定死了,可以立即加入vector
{
v[t].push_back(make_pair(-pre[t][f(a, t)], 0));
flag[t][f(a, t)] = true;
}
}
for (int i = 1; i <= N; i++)
{
if (pre[t][i] < 0 && !flag[t][i])
v[t].push_back(make_pair(-pre[t][i], 1));
}
sort(v[t].begin(), v[t].end()); // pair自动按照字典序排序,相同个数的连通分量,环都在前面
}
if (v[0].size() != v[1].size())
{
printf("Case #%d: NO\n", num);
continue;
}
for (int i = 0; i < v[0].size(); i++)
{
if (v[0][i] != v[1][i])
{
printf("Case #%d: NO\n", num);
break;
}
if (i == v[0].size() - 1)
printf("Case #%d: YES\n", num);
}
}
return 0;
}