【图论】Prufer序列

凯莱定理:n个点的完全图的生成树有n^(n-2)个。

长度为n-2,值域为[1,n]的Prufer序列和一个n个节点编号分别为[1,n]的无向连通图的生成树双射。

无根树变Prufer:每次选择一个编号最小的叶子删除他,在序列中记录其父亲,直到剩下最后的两个点。(假如再做一步,则Prufer序列一定以n号点结尾,毫无意义)
用个堆也可以,直接用一个指针也可以。

下面把n-1位置也放上Prufer,为了统一处理。

            for(int i = 1; i <= n; ++i) {
                deg[i] = G[i].size();
                vis[i] = 0;
                P[i] = 0;
            }
            int top = 0, cur = 1;
            while(top <= n - 2) {
                while(deg[cur] != 1)
                    ++cur;
                int u = cur;
                do {
                    for(int v : G[u]) {
                        if(vis[v] == 1)
                            continue;
                        --deg[v];
                        --deg[u];
                        P[++top] = v;
                        vis[u] = 1;
                        u = v;
                        break;
                    }
                } while(u < cur && deg[u] == 1 && top <= n - 2);
            }
            assert(P[n - 1] == n);

每个点在Prufer中出现的次数+1就是其度数,叶子节点不存在于序列中。

Prufer变无根树:每次选择一个剩余度数为1的最小的点,把这个点删除,然后把这个点和Prufer当前的点连接,然后两端度数-1。重复n-2次后,剩下两个度数为1的点,把他们连起来。

这个也是用个堆来做最简单,或者用个指针来做也可以。

下面把n-1位置也放上Prufer,为了统一处理,但是统计度数的时候不能算上它。

            P[n - 1] = n;
            for(int i = 1; i <= n; ++i) {
                deg[i] = 1;
                vis[i] = 0;
                G[i].clear();
            }
            for(int i = 1; i <= n - 2; ++i)
                ++deg[P[i]];
            int top = 0, cur = 1;
            while(top <= n - 2) {
                while(deg[cur] != 1)
                    ++cur;
                int u = cur;
                do {
                    int v = P[++top];
                    --deg[v];
                    --deg[u];
                    G[u].eb(v);
                    G[v].eb(u);
                    u = v;
                } while(u < cur && deg[u] == 1 && top <= n - 2);
            }
posted @ 2021-01-17 18:39  purinliang  阅读(137)  评论(0编辑  收藏  举报