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个人博客,仅供学习讨论


posted @ 2022-08-31 17:06  hh2048  阅读(62)  评论(0编辑  收藏  举报