环和链的判断
Hand in Hand HDU - 3926
给一一副有单环或链的图 判断是否同构
我们可以判断环有多少个,链有多少个,同时判断一下每个环的点数和链的点的个数
第一种做法:
所以,我们可以直接用并查集来做.
同时,我们注意到两幅图的人数应该是一样的.
所以,把并查集修改一下就直接判断了. (如果成环,最后成环的并集会让点的个数加倍. 同时,因为人数是固定的,所以可以直接sort,然后比较)
#include <bits/stdc++.h> using namespace std; int fa[10050]; int id1[10050], id2[10050]; int find(int idx) { return fa[idx] < 0 ? idx : fa[idx] = find(fa[idx]); } inline void Union(int x, int y) { int xf = find(x); int yf = find(y); if (fa[xf] > fa[yf]) swap(xf, yf); fa[xf] = fa[xf] + fa[yf]; if (xf == yf) return ; // 注意 放到这个位置的意义,就是为了判环. fa[yf] = xf; } int main() { int t, n, m, i, v, u, cnt1, cnt2; scanf("%d", &t); for (int cas=1; cas<=t; ++cas) { memset(fa, -1, sizeof(fa)); scanf("%d%d",&n, &m); for (i=1; i<=m; ++i) { scanf("%d%d", &u, &v); Union(u, v); } cnt1 = cnt2 = 0; for (i=1; i<=n; ++i) if (fa[i]<0) id1[cnt1++] = abs(fa[i]); memset(fa, -1, sizeof(fa)); scanf("%d%d", &n, &m); for (i=1; i<=m; ++i) { scanf("%d%d", &u, &v); Union(u, v); } for (i=1; i<=n; ++i) if (fa[i]<0) id2[cnt2++] = abs(fa[i]); printf("Case #%d: ", cas); if (cnt1 != cnt2) { printf("NO\n"); continue; } sort(id1, id1+cnt1); sort(id2, id2+cnt2); for (i=0; i<cnt1; ++i) if (id1[i] != id2[i]) break; if (i==cnt1) printf("YES\n"); else printf("NO\n"); } return 0; }
第二种做法:
我们注意到别个点的度数都只有2.所以我们Tarjan可以根据点的个数和度数来判环.
用Tarjan求连通分量, 同时将每个环和链的点数分别求出.
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; struct Edge { int lst; int to; }; class TARJAN { public: Edge edge[20500]; int n, m; int head[10500]; int qsz; int dfn[10500]; int dfn_num; int low[10500]; int inSt[10500]; int st[10500]; int top; int cir[10500]; int line[10500]; int in[10500]; int cir_cnt, line_cnt; void init() { qsz = 1; cir_cnt = line_cnt = 0; dfn_num = top = 0; memset(head, 0, sizeof(head)); memset(dfn, 0, sizeof(dfn)); memset(in, 0, sizeof(in)); } inline void add(int u, int v) { edge[qsz].lst = head[u]; edge[qsz].to = v; head[u] = qsz++; } void build() { int i, u, v; scanf("%d%d", &n, &m); for (i=1; i<=m; ++i) { scanf("%d%d", &u, &v); add(u, v); add(v, u); in[u]++; in[v]++; } } void Tarjan(int u) { int v, i; dfn[u] = low[u] = ++dfn_num; st[++top] = u; inSt[u] = true; for (i=head[u]; i; i=edge[i].lst) { v = edge[i].to; if (!dfn[v]) { Tarjan(v); low[u] = min(low[u], low[v]); } else if (inSt[v]) { low[u] = min(low[u], dfn[v]); } } if (dfn[u] == low[u]) { int cnt_node = 0; int cnt_in = 0; do { inSt[st[top]] = false; cnt_node++; cnt_in += in[st[top]]; } while (st[top--] != u); if (cnt_node*2 == cnt_in) // 这个题度数都只有2 所以如果节点数乘以2等于所有点入度,那么就成环. cir[cir_cnt++] = cnt_node; else line[line_cnt++] = cnt_node; } } void _get() { init(); build(); for (int i=1; i<=n; ++i) if (!dfn[i]) Tarjan(i); } }t1, t2; int main() { int t, n, m, u, v, i, cas; scanf("%d", &t); for (cas=1; cas<=t; cas++) { t1._get(); t2._get(); printf("Case #%d: ", cas); if (t1.cir_cnt!=t2.cir_cnt || t1.line_cnt!=t2.line_cnt) { printf("NO\n"); continue; } sort(t1.cir, t1.cir+t1.cir_cnt); sort(t2.cir, t2.cir+t2.cir_cnt); bool flag = true; for (i=0; i<t1.cir_cnt; ++i) { if (t1.cir[i] != t2.cir[i]) { flag = false; break; } } if (flag) { sort(t1.line, t1.line+t1.line_cnt); sort(t2.line, t2.line+t2.line_cnt); for (i=0; i<t1.line_cnt; ++i) { if (t1.line[i] != t2.line[i]) { flag = false; break; } } } if (flag) printf("YES\n"); else printf("NO\n"); } return 0; }