HDU 3926 - Hand in Hand(图论 + DSU)(同构图)
HDU 3926 - Hand in Hand(⇔源地址)
目录
tag
⇔图论、⇔DSU。
题意
同构图的定义:两张图拥有相同的环与链,且每个环、每条链上的点的数量相同。
以下样例均为同构图:
补一道同构图的题目:2018暑假牛客多校(一)D 。
有一群小朋友手牵手,规定一只手最多抓住一只手,这样会形成一张图。重复两次,询问这两次构成的两张图是否是同构的。
思路
很显然,这样构成的图一定只包含简单环和简单链。
第一轮的想法(图上BFS)
直接建图,分别对于链、环跑 BFS
(类似于 topsort
),统计每条链、每个环上点的数量。
看起来很对,但是超时了。
第二轮的想法(DSU)
使用DSU合并点,同时标记环的父节点。随后遍历所有的点,只要其父节点被标记就说明这个点在环上,如此,分别统计每条链、每个环上点的数量。
AC代码
点击查看代码
第一轮的想法(图上BFS)
struct Graph { //Graph,链式前向星封装
int tot, h[N], ver[M], ne[M];
int deg[N], edge[M]; //入度与边权
int v[N], V[N];
Graph(int n) {
tot = 0; //多组样例清空
FOR (i, 1, n) {
h[i] = v[i] = V[i] = deg[i] = 0;
}
}
void add(int x, int y) {
ver[++ tot] = y, ne[tot] = h[x], h[x] = tot;
++ deg[y];
}
void dfs(int x) {
v[x] = 1;
for (int i = h[x]; i; i = ne[i]) {
int y = ver[i];
if (v[y]) continue;
dfs(y);
}
}
int bfs(int s) {
queue<int> q; q.push(s);
V[s] = 1;
int cnt = 0;
while (!q.empty()) {
int x = q.front(); q.pop();
++ cnt;
for (int i = h[x]; i; i = ne[i]) {
int y = ver[i];
if (V[y]) continue; V[y] = 1;
q.push(y);
}
}
return cnt;
}
};
void Solve() {
int n1, m1; cin >> n1 >> m1;
Graph g1(n1 + 5);
FOR (i, 1, m1) {
int x, y; cin >> x >> y;
g1.add(x, y), g1.add(y, x);
}
int n2, m2; cin >> n2 >> m2;
Graph g2(n1 + 5);
FOR (i, 1, m2) {
int x, y; cin >> x >> y;
g2.add(x, y), g2.add(y, x);
}
vector<int> chain1, chain2, loop1, loop2;
FOR (i, 1, n1) {
if (g1.deg[i] == 1 && g1.V[i] == 0) {
chain1.push_back(g1.bfs(i)); //BFS链
}
}
FOR (i, 1, n1) {
if (g1.V[i] == 0) {
loop1.push_back(g1.bfs(i)); //BFS环
}
}
FOR (i, 1, n2) {
if (g2.deg[i] == 1 && g2.V[i] == 0) {
chain2.push_back(g2.bfs(i)); //BFS链
}
}
FOR (i, 1, n2) {
if (g2.V[i] == 0) {
loop2.push_back(g2.bfs(i)); //BFS环
}
}
sort(ALL(loop1));
sort(ALL(loop2));
sort(ALL(chain1));
sort(ALL(chain2));
if (n1 == n2 && m1 == m2 && loop1 == loop2 && chain1 == chain2) cout << "YES\n";
else cout << "NO\n";
}
第二轮的想法(DSU)
struct DSU {
vector<int> fa;
DSU(int n) : fa(n) {
iota(ALL(fa), 0);
}
int get(int x) {
while (x != fa[x]) x = fa[x] = fa[fa[x]];
return x;
}
bool merge(int x, int y) { //x是y的祖先
x = get(x), y = get(y);
if (x == y) return false;
fa[y] = x;
return true;
}
bool same(int x, int y) {
return get(x) == get(y);
}
};
void Solve() {
int n1, m1; scanf("%d %d", &n1, &m1);
map<int, int> is_loop1;
DSU ver1(n1 + 5);
FOR (i, 1, m1) {
int x, y; scanf("%d %d", &x, &y);
if (ver1.same(x, y)) {
is_loop1[ver1.get(x)] = 1;
}
ver1.merge(x, y);
}
int n2, m2; scanf("%d %d", &n2, &m2);
map<int, int> is_loop2;
DSU ver2(n2 + 5);
FOR (i, 1, m2) {
int x, y; scanf("%d %d", &x, &y);
if (ver2.same(x, y)){
is_loop2[ver2.get(x)] = 1;
}
ver2.merge(x, y);
}
if (n1 != n2 || m1 != m2 || is_loop1.sz != is_loop2.sz) { //剪枝1
cout << "NO\n";
RE;
}
vector<int> loop1(n1 + 5), loop2(n1 + 5), chain1(n2 + 5), chain2(n2 + 5);
FOR (i, 1, n1) {
int fa = ver1.get(i);
if (is_loop1.count(fa)) ++ loop1[fa];
else ++ chain1[fa];
}
FOR (i, 1, n2) {
int fa = ver2.get(i);
if (is_loop2.count(fa)) ++ loop2[fa];
else ++ chain2[fa];
}
if (chain1.sz != chain2.sz || loop1.sz != loop2.sz) { //剪枝2
cout << "NO\n";
RE;
}
sort(ALL(loop1));
sort(ALL(loop2));
sort(ALL(chain1));
sort(ALL(chain2));
if (loop1 == loop2 && chain1 == chain2) cout << "YES\n";
else cout << "NO\n";
}
错误次数:5
前两次没搞清楚什么是同构图,自己随便写了一发,炸了。
第三次在判环的时候出错了:一边读入一边存环;同时并查集的板子还错了(多次定义用的同一个数组)。
第四、五次用BFS做的,超时了。
文 / WIDA
2022.08.31 成文
首发于WIDA个人博客,仅供学习讨论