• 链表加bfs求补图联通块


    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
    ", 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
    ", 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
    ", block_cnt);
            for(int i = 1; i <= block_cnt; i++){
                printf("%d%c", num[i], i == block_cnt ? '
    ' : ' ');
            }
        }
        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
    搞图论是没有用的,转行做数学题了hh
  • 相关阅读:
    Redis
    Redis入门
    C#编程--语句(分支语句)
    C#编程--运算符
    C#编程--输入和输出
    C#编程进制转换
    C#语言课程11月10日
    C#语言课程11月9日
    C#语言课程11月7日
    C#语言课程11月6日
  • 原文地址:https://www.cnblogs.com/DearDongchen/p/9211513.html
Copyright © 2020-2023  润新知