并查集运用,最后那一步并查集判断个人觉得有点儿小微妙。
/* *State: HDU3560 234MS 1812K 1260 B C++ *题目大意: * 给定一个无向图,求无向图的连通分量的数量,还有连通分量是一个环的 * 数量。 *解题思路: * 各种解法都行,用并查集比较简单。还可以用广搜,还可以用深搜,但是写起来麻烦。 * 而且广搜跟深搜的时间复杂度是O(v + e),但是并查集的时间复杂度通过有效地修改 * 可以改至接近线性,采用启发式合并还有路径压缩,还可以到达O(1),优雅至极。 *题目困惑: * 用并查集开始写,应该是很清晰的思路的,结果因为初始化,各种TLE.怎么都没有想到 * 是因为初始化导致的TLE,白白贡献了4~5个TLE,囧。不过想不通100000的点,我初始化 * 哪里会TLE啦。以后要多注意下初始化了。多用脑袋跟时间还内存斤斤计较哈。 */
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> using namespace std; const int MAXN = 100005; typedef struct _node { int p, rank; }N; N uqSet[MAXN]; int vst[MAXN], du[MAXN]; void init(int n) { for(int i = 0; i < n; i++) { du[i] = vst[i] = 0; uqSet[i].p = i; uqSet[i].rank = 0; } } int findSet(int x) { int f = x, t; while(x != uqSet[x].p) x = uqSet[x].p; while(f != x) { t = uqSet[f].p; uqSet[f].p = x; f = t; } return x; } void Union(int x, int y) { x = findSet(x); y = findSet(y); if(x == y) return ; if(uqSet[x].rank > uqSet[y].rank) { uqSet[y].p = x; } else { if(uqSet[x].rank == uqSet[y].rank) uqSet[y].rank++; uqSet[x].p = y; } } int main(void) { #ifndef ONLINE_JUDGE //freopen("in.txt", "r", stdin); #endif int n, m; while(scanf("%d %d", &n, &m), n || m) { init(n); int u, v; for(int i = 0; i < m; i++) { scanf("%d %d", &u, &v); du[u]++, du[v]++; Union(u, v); } int com = 0; int reduce = 0; for(int j = 0; j < n; j++) { if(findSet(j) == j) com++; if(vst[findSet(j)] == 0 && du[j] != 2) { vst[findSet(j)] = 1; reduce++; } } printf("%d %d\n", com, com - reduce); } return 0; }