可平面性判定,任意平面圖判定(代碼實現)


算法來源: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\n");
            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\n");
        else printf("NO\n");
    }
    return 0;
}

題目是HNOI2010d的Planar,只不過沒有讀入哈密頓回路

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

哈密頓回路做法的時間:

 

 路徑嵌入法的時間

 

 總感覺時間多了不少啊。。。。。。不過想想直接尋找哈密頓回路的時間復雜度——

如果要判斷一般圖的平面性,還是選擇路徑嵌入法吧


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM