• 可平面性判定,任意平面图判定(代码实现)


    算法来源:http://xueshu.baidu.com/usercenter/paper/show?paperid=66095d509501f2533f8bee9cf9988d88&site=xueshu_se

    可平面性算法——路径嵌入法

    要判断一个图是否为平面图,在考虑路径嵌入法之前,先考虑其他的优化

    1.根据欧拉定理:$n(点数)-e(边数)+f(面数)=w(连通块)+1$,而一个面最少由三个面组成,一个边属于两个面,得到$3f>=2e$,又$w>=1$得到$f<=2n-4$,$e<=3n-6$

    所以如果边数超过了就直接判断False吧(面数不太好判断)

    2.根据库拉托夫斯基定理:一个图的所有子图经过缩点(将所有度为2的点去掉并连接它的相邻两个点)后均不为K5(五个点的完全图)或K33(两边都是三个点的完全二分图),那么这个图就是可平面图

    由此可以得到:对于两个可平面图A,B,任意连接小于等于两条从A到B的线段后得到的图仍然是一个平面图。(感性的理解,加入的这两条边并不能组成K5或者K33的任意一个部分)

    这样理论上就可以把图分成一个个“边三联通分量”,当然我们只分成边双联通分量就可以了。

    现在开始路径嵌入法吧!

    路径嵌入法的算法流程是这样的:

    pre:先把上面的1.2两点优化搞了

    1.取出你的边双联通分量,记为G

    2.在G中选出任意一个回路H,在G中去掉这个回路,将这个回路嵌入图中,并将G分成若干个连通块$B_1$~$B_n$,这些连通块以边为联通,以已经嵌入的点作为分隔,每个连通块的边界(即已经嵌入的点)称为这个连通块的附着点

    3.对所有的联通块计算一个值$F(B_i)$,这个值是已经嵌入的面中能够包含$B_i$所有附着点的面的数量

    4.如果有一个$F(B_i)=0$,那么就不能再嵌进去了,返回False,而对于$F=1$和$F>1$的连通块来说,先嵌入$F=1$的,再嵌入$F>1$的,证明详见论文

    5.现在将一个连通块嵌入图中,首先在连通块中找出一条路径,这条路径的两个端点都是附着点,将这个路径嵌入图中,并将去掉这个路径的连通块又分为若干个连通块,返回第三步直到所有连通块都嵌入图中后结束

    举个例子吧~

    输入数据:

    9 20
    1 2
    2 3
    4 5
    5 6
    7 8
    8 9
    1 4
    4 7
    2 5
    5 8
    3 6
    6 9
    1 5
    2 4
    2 6
    3 5
    4 8
    5 7
    5 9
    6 8

    这张图长这样:

     任意找一回路:

     分成若干个连通块(注意连通块是边集):

    $B_1={(1,5)},B_2={(3,5)},B_3={(2,5)},B_4={(4,2)},B_5={(6,2)},B_6={(5,7),(4,7),(7,8),(5,8),(4,8),(5,9),(9,8),(9,6),(6,8)}$

    举个附着点的例子吧:当前$B_1$的附着点是${1,5},B_5$的附着点是${4,5,6}$

    当前的面(有序点集)是:

    $P_1={4,1,2,3,6,5},P_2={4,1,2,3,6,5}$(两个面一个是外面一个是里面,点集表示相同)

    当前所有连通块的F值都是2,所以任意取一个连通块(例如$B_6$)

    在其中取一条路径嵌入:

    弹出$B_6$,生成$B_7={(5,7)},B_8={(4,8)},B_9={(6,8)},B_{10}={(5,9),(9,8),(9,6)}$ 

    当前的面为$P_1={4,1,2,3,6,5,8,4},P_2={5,4,7,8},P_3={4,1,2,3,6,5}$

    计算F值:$F(B_1)=2,F(B_2)=2,F(B_3)=2,F(B_4)=2,F(B_5)=2,F(B_7)=2,F(B_8)=2,F(B_9)=1,F(B_{10})=1$

    所以要先嵌入$B_9$和$B_{10}$,$B_9$嵌完后就完了,$B_{10}$嵌一条路径又会分出一个小连通块...

    最终按照程序嵌入下去直到连通块数量为0,判断这个图——是平面图!

    算法复杂度分析:这个算法的复杂度和实现有着密不可分的关系,但由于代码实在过于复杂(或者说找不到合适的数据结构来维护?),大致分析复杂度在$O(n^2)$到$O(n^3)$之间,但实际运行时由于优化很多(这些优化大多都是能够明显加快速度但理论分析却省不了时间),尤其是随机数据的表现极其良好,几乎可以当做$O(n^2)$来看待

    最后的最后,给出大常数+冗长+STL依赖症患者+诡异的实现方式代码

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    #include <vector>
    #include <map>
    #include <list>
    using namespace std;
    
    inline long long read(){
        long long ans = 0, f = 1;
        char ch = getchar();
        while(!isdigit(ch))
            f *= (ch == '-') ? -1 : 1, ch = getchar();
        do ans = (ans << 1) + (ans << 3) + (ch ^ 48), ch = getchar();
        while(isdigit(ch));
        return ans * f;
    }
    
    const int MAXN = 205;
    int sta[MAXN], dfn[MAXN], low[MAXN], vis[MAXN], isEmbed[MAXN];
    vector<int> Plane[MAXN<<1], book[MAXN<<1];
    int PlaneNum = 1;
    
    struct Graph{
        map<int, int> head;
        vector<int> next, last, val, att;
        int atp, atpPos;
        void clear(){
            head.clear(), next.clear(), last.clear(), val.clear(), att.clear(), atp = atpPos = 0;
            next.push_back(0), last.push_back(0), val.push_back(0);
            next.push_back(0), last.push_back(0), val.push_back(0);
        }
        Graph(){clear();}
        void add(int x,int y){
            next.push_back(head[x]), last.push_back(y), val.push_back(1), head[x] = next.size() - 1;
        }
        const bool operator < (const Graph &temp) const{
            return atp < temp.atp;
        }
    }Tot;
    
    void getAtp(Graph &G){
        sort(G.att.begin(), G.att.end()), G.atp = 0;
        for(int i=1; i<=PlaneNum; i++){
            if(book[i].size() < G.att.size()) continue;
            int now = 0;
            for(int j=0; j<G.att.size(); j++){
                while(now < book[i].size() - 1 && book[i][now] < G.att[j]) now++;
                if(book[i][now] != G.att[j]) break;
                else if(j == G.att.size() - 1)
                    G.atp++, G.atpPos = i;
            }
        }
    }
    
    void embed(int pos){
        for(int i=1; i<=sta[0]; i++) isEmbed[sta[i]] = true;
        int l = 0, r = Plane[pos].size() - 1;
        while(Plane[pos][l] != sta[1] && Plane[pos][l] != sta[sta[0]]) l++;
        while(Plane[pos][r] != sta[1] && Plane[pos][r] != sta[sta[0]]) r--;
        vector<int> temp1, temp2;
        for(int i=0; i<l; i++) temp1.push_back(Plane[pos][i]);
        if(Plane[pos][l] == sta[1]) for(int i=1; i<=sta[0]; i++) temp1.push_back(sta[i]);
        else for(int i=sta[0]; i>=1; i--) temp1.push_back(sta[i]);
        for(int i=r+1; i<Plane[pos].size(); i++) temp1.push_back(Plane[pos][i]);
        for(int i=r-1; i>l; i--) temp2.push_back(Plane[pos][i]);
        if(Plane[pos][l] == sta[1]) for(int i=1; i<=sta[0]; i++) temp2.push_back(sta[i]);
        else for(int i=sta[0]; i>=1; i--) temp2.push_back(sta[i]);
        Plane[pos] = book[pos] = temp1, ++PlaneNum;
        Plane[PlaneNum] = book[PlaneNum] = temp2;
        sort(book[pos].begin(), book[pos].end()), sort(book[PlaneNum].begin(), book[PlaneNum].end());
    }
    
    bool match(int x,int goal,Graph &G){
        vis[x] = true;
        for(int l=G.head[x]; l; l=G.next[l]){
            int y = G.last[l];
            if(vis[y]) continue;
            if(y == goal || (!isEmbed[y] && match(y, goal, G))){
                G.val[l] = G.val[l^1] = 0;
                if(y == goal) sta[++sta[0]] = y;
                sta[++sta[0]] = x;
                return true;
            }
        }
        return false;
    }
    
    void findGraph(Graph &G,int l,Graph &ret){
        int x = G.last[l], fa = G.last[l^1];
        ret.add(x, fa), ret.add(fa, x), G.val[l] = G.val[l^1] = 0;
        if(!isEmbed[x]) for(int lk=G.head[x]; lk; lk=G.next[lk]){
            if(G.val[lk]) findGraph(G, lk, ret);
        }else if(!vis[x])
            ret.att.push_back(x), vis[x] = true;
    }
    
    bool Solve(list<Graph> &Lis){
        if(!Lis.size()) return true;
        list<Graph>::iterator it = Lis.begin();
        int cnt = Lis.size() - 1;
        while(!Lis.empty()){
            Graph &Now = *it;
            getAtp(Now), cnt++;
            if(!Now.atp) return false;
            if(cnt == Lis.size() || Now.atp == 1){
                memset(vis, 0, sizeof(vis));
                sta[0] = 0, match(Now.att[0], Now.att[1], Now);
                embed(Now.atpPos), memset(vis, 0, sizeof(vis));
                for(int j=2; j<sta[0]; j++) for(int l=Now.head[sta[j]]; l; l=Now.next[l]) if(Now.val[l]){
                    Graph temp;
                    findGraph(Now, l, temp);
                    if(!vis[sta[j]]) temp.att.push_back(sta[j]);
                    for(int k=0; k<temp.att.size(); k++) vis[temp.att[k]] = 0;
                    Lis.push_back(temp);
                }
                list<Graph>::iterator temp = it++;
                Lis.erase(temp), cnt = 0, it--;
            }
            it++;
            if(it == Lis.end()) it = Lis.begin();
        }
        return true;
    }
    
    void Tarjan(int x,int fa,vector<Graph> &ret){
        dfn[x] = low[x] = ++dfn[0];
        for(int l=Tot.head[x]; l; l=Tot.next[l]){
            int y = Tot.last[l];
            if(y == fa) continue;
            if(!dfn[y]) Tarjan(y, x, ret), low[x] = min(low[x], low[y]);
            else low[x] = min(low[x], dfn[y]);
        }
        if(dfn[x] <= low[x]){
            Graph temp;
            for(int l=Tot.head[x]; l; l=Tot.next[l]) if(Tot.val[l] && dfn[Tot.last[l]] > dfn[x])
                findGraph(Tot, l, temp);
            ret.push_back(temp);
        }
    }
    
    void findCircle(Graph &G){
        int x = G.last[2];
        while(!vis[x]){
            vis[x] = true;
            for(int l=G.head[x]; l; l=G.next[l]) if((l ^ 1) != sta[sta[0]]){
                x = G.last[l], sta[++sta[0]] = l;
                break;
            }
        }
        int l = 1, r = sta[0];
        while(G.last[sta[l] ^ 1] != x) l++;
        sta[0] = 0;
        for(int i=l; i<=r; i++)
            G.val[sta[i]] = G.val[sta[i] ^ 1] = 0, sta[++sta[0]] = G.last[sta[i] ^ 1];
    }
    
    int main(){
        int T = read();
        while(T--){
            int n = read(), m = read();
            vector<Graph> Div;
            Tot.clear();
            for(int i=1; i<=m; i++){
                int x = read(), y = read();
                if(x == y) continue; 
                Tot.add(x, y), Tot.add(y, x);
            }
            for(int i=1; i<=n; i++)
                read();
            if(m > 3 * n - 6 && m > 1){
                printf("NO
    ");
                continue;
            }
            memset(dfn, 0, sizeof(dfn));
            memset(low, 0, sizeof(low));
            memset(isEmbed, 0, sizeof(isEmbed));
            memset(vis, 0, sizeof(vis));
            for(int i=1; i<=n; i++) if(!dfn[i])
                Tarjan(i, -1, Div);
            bool flag = true;
            for(int i=0; i<Div.size(); i++){
                if(!Div[i].head.size()) continue; 
                sta[0] = 0, findCircle(Div[i]);
                Plane[1].push_back(sta[1]), Plane[1].push_back(sta[sta[0]]);
                embed(1);
                list<Graph> ret;
                memset(vis, 0, sizeof(vis));
                for(int j=1; j<=sta[0]; j++) for(int l=Div[i].head[sta[j]]; l; l=Div[i].next[l]) if(Div[i].val[l]){
                    Graph temp;
                    findGraph(Div[i], l, temp);
                    if(!vis[sta[j]]) temp.att.push_back(sta[j]);
                    for(int k=0; k<temp.att.size(); k++) vis[temp.att[k]] = 0;
                    ret.push_back(temp);
                }
                flag &= Solve(ret);
                for(int j=1; j<=PlaneNum; j++) Plane[j].clear(), book[j].clear();
                PlaneNum = 1;
                if(!flag) break;
            }
            if(flag) printf("YES
    ");
            else printf("NO
    ");
        }
        return 0;
    }

    题目是HNOI2010d的Planar,只不过没有读入哈密顿回路

    洛谷连接:https://www.luogu.com.cn/problem/P3209

    哈密顿回路做法的时间:

     路径嵌入法的时间

     总感觉时间多了不少啊。。。。。。不过想想直接寻找哈密顿回路的时间复杂度——

    如果要判断一般图的平面性,还是选择路径嵌入法吧

  • 相关阅读:
    Redhat安装配置VNC服务器
    Linux下如何强制中断一个程序的执行?
    Linux上怎么快速删除一个目录
    Linux两台服务器上互传文件
    Linux压缩解压命令
    新Linux系统配置yum源
    从零开始,搭建博客系统MVC5+EF6搭建框架(1),EF Code frist、实现泛型数据仓储以及业务逻辑
    [转]关于有偿提供拼图响应式后台的通知
    ASP.NET MVC post请求接收参数的三种方式
    ASP.NET MVC中获取URL地址参数的两种写法
  • 原文地址:https://www.cnblogs.com/PHDHD/p/12747430.html
Copyright © 2020-2023  润新知