標籤:鏈表 迴圈 span pop 探索 int 聯通 分享圖片 入隊
https://oj.neu.edu.cn/problem/1387
給一個點數N <= 100000, 邊 <= 1000000的無向圖,求補圖的聯通塊數,以及每個塊包含的點數
由於點數太大,補圖會是稠密圖,甚至建立補圖都要O(n^2),只能挖掘一下聯通塊,bfs,補圖的性質,從原圖入手求補圖的聯通塊:
在原圖中不直接相鄰的點,在補圖中一定屬於同一個聯通塊
每個點只屬於一個聯通塊,所以找好一個聯通塊之後可以刪去這個聯通塊的所有點,把圖規模縮小
這樣子:1.準備一個集合放所有未探索的點,初始化時將1~N放進去
2.從集合中取一點放入隊列(新的聯通塊)
3.當隊列不為空白時,從隊列中取一個點u並彈出,將原圖中與u直接相連的點標記;遍曆集合,將在集合中的(即未探索的)並且未被標記的點(這些點屬於本聯通塊)入隊並從集合中刪去,將標記刪去。重複執行直到隊列為空白
4.集合不為空白轉2,為空白結束
考慮有刪除操作和時間問題,集合的實現當然是選擇鏈表,用數組實現的雙向鏈表即可
最佳化有兩個:一是通過原圖找補圖的聯通塊;二是把搜過的點刪除,這樣每次找未標記的點時比起從1迴圈到N更優(常數最佳化(誤))
#include<cstdio>#include<cstring>#include<algorithm>#include<iostream>#include<queue>using namespace std;const int maxn = 1e5+100, maxm = 1e6+100, inf = 0x3f3f3f3f;struct lnk{ int val; int pre, nxt;}lk[maxn];struct edge{ int v, nxt;}e[maxm*2];int head[maxn], tot, block_cnt, n, m;int adj[maxn], vis[maxn], num[maxn];void addedge(int u, int v){ e[tot] = (edge){v, head[u]}; head[u] = tot++;}void dele(int x){ lk[lk[x].nxt].pre = lk[x].pre; lk[lk[x].pre].nxt = lk[x].nxt;}void src(){ for(int i = 1; i <= n; i++){ vis[i] = adj[i] = 0; } queue<int>Q; block_cnt = 0; while(lk[0].nxt != -1){ //puts("blk++"); Q.push(lk[0].nxt); //printf("take %d\n", lk[0].nxt); vis[lk[lk[0].nxt].val] = 1; dele(lk[0].nxt); block_cnt++; num[block_cnt] = 1; while(!Q.empty()){ int x = Q.front(); x = lk[x].val; //printf("%d\n", x); Q.pop(); for(int i = head[x]; ~i; i = e[i].nxt){ int v = e[i].v; adj[v] = 1; } for(int i = lk[0].nxt; ~i; i = lk[i].nxt){ int w = lk[i].val; if(!vis[w] && !adj[w]){ Q.push(w); vis[w] = 1; dele(i); num[block_cnt]++; } } for(int i = head[x]; ~i; i = e[i].nxt){ int v = e[i].v; adj[v] = 0; } } }}int main(){ int t; scanf("%d", &t); while(t--){ scanf("%d%d", &n, &m); for(int i = 1; i <= n; i++) head[i] = -1; tot = 0; while(m--){ int u, v; scanf("%d%d", &u, &v); addedge(u, v); addedge(v, u); } for(int i = 1; i <= n; i++){ lk[i].val = i; lk[i].pre = i-1; lk[i].nxt = i+1; } lk[n].nxt = -1; lk[0].nxt = 1; src(); sort(num+1, num+block_cnt+1); printf("%d\n", block_cnt); for(int i = 1; i <= block_cnt; i++){ printf("%d%c", num[i], i == block_cnt ? ‘\n‘ : ‘ ‘); } } return 0;}/* 3 5 7 1 2 1 3 1 4 1 5 2 3 2 4 2 5 6 9 1 4 1 5 1 6 2 4 2 5 2 6 3 4 3 5 3 6 3 3 1 2 2 3 3 1 */
View Code
鏈表加bfs求補圖聯通塊