【BZOJ 1098】办公楼(补图连通块个数,Bfs)
補圖連通塊個數這大概是一個套路吧,我之前沒有見到過,想了好久都沒有想出來QaQ
事實上這個做法本身就是一個樸素算法,但進行巧妙的實現,就可以分析出它的上界不會超過 $O(n + m)$。
接下來介紹一下這個技巧:
很顯然一個不在原圖中的邊一定在補圖中出現,如果我們考慮用樸素的$Bfs$求一個圖中的連通塊個數,對于當前的一個點$x$,枚舉它連出去的邊進行拓展即可。
如果是求補圖中的,那對于當前隊首的點$x$,可以枚舉其他所有的點,看是否和這個點有連邊,沒有就拓展。
一個可以的優化就是,一個點在$Bfs$是不會被入隊多次,我們可以把它刪掉,這樣可以優化原先樸素算法在拓展上$O(n)$枚舉的復雜度。
至于為什么刪掉不會造成問題,我們可以考慮它有可能存在的問題就是某一個點$x$在拓展的時候準備拓展一個已經被刪除的點$y$,那可能會造成原本在一個連通塊的點分成多個,這種情況下在之前$y$拓展的時候就會先拓展到$x$;至于如果兩個點同時被刪除時,那它們一定是同一個根,一定是同一個連通塊里的。
接下來證明這樣優化之后復雜度的上界是$O(n + m)$:
事實上我們進行每一次拓展的時候,一次失敗的拓展是因為原圖中存在著這條邊,而一旦拓展成功,就能刪掉一個點。由于每條邊做多被訪問一次,于是每次拓展就是要么是減少一條邊,要么是減少一個點,故$Bfs$的復雜度上界是$O(n + m)$的。
?
這個題就是一個例子,在此做一個總結:
#include <cstdio> #include <queue> #include <algorithm>using namespace std;const int N = 100005;int n, m; int fa[N], vis[N]; vector<int> g[N], ans;int Sk(int x) {return fa[x] == x? x : fa[x] = Sk(fa[x]); }void Bfs(int s) {static queue<int> Q;Q.push(s), fa[s] = Sk(s + 1);ans.push_back(1);for (int x; !Q.empty(); ) {x = Q.front(), Q.pop();for (int v : g[x]) vis[v] = x;for (int i = Sk(1); i <= n; i = Sk(i + 1)) {if (vis[i] != x) fa[i] = Sk(i + 1), Q.push(i), ++ans.back();}} }int main() {scanf("%d%d", &n, &m);for (int i = 1, x, y; i <= m; ++i) {scanf("%d%d", &x, &y);g[x].push_back(y), g[y].push_back(x);}for (int i = 1; i <= n; ++i) fa[i] = i;fa[n + 1] = n + 1;for (int i = 1; i <= n; i = Sk(i + 1)) Bfs(i);sort(ans.begin(), ans.end());printf("%d\n", (int)ans.size());for (int i : ans) printf("%d ", i);putchar('\n');return 0; } View Code?
轉載于:https://www.cnblogs.com/Dance-Of-Faith/p/9648930.html
總結
以上是生活随笔為你收集整理的【BZOJ 1098】办公楼(补图连通块个数,Bfs)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【目录】《剑指Offer》Java实现
- 下一篇: 2018091-2 博客作业