图的可行遍性

1.欧拉图

欧拉回路:从图上一点出发,经过所有的边必须且只能一次,最终回到起点的路径
欧拉图:有欧拉回路的图

欧拉图要满足两个条件:
  1. 连通,不能有孤立的点存在,即没有度数为0的点
  2. 对于无向图来说,度数为奇数的点的个数为0;对于有向图来说,每个点的入度要等于出度
欧拉图的变形—— “一笔画问题” (witness手滑):可以不回到原点,但依然要经过所有的边必须且只能一次,这样同样要满足两个条件:
  1. 连通,同上
  2. 对于无向图来说,度数为奇数的点的个数为2,且这两个点一定是一笔画的起点和终点;对于有向图来说,存在两个顶点,其入度不等于出度,其中一点出度比入度大1,为路径的起点,另外一点入度比出度大1,为路径的终点。
求欧拉回路的算法:直接对边进行dfs,回溯的顺序就是欧拉回路
由于一笔画可以通过预处理确定起点和终点,那么直接对起点或终点进行边dfs即可

求边的序列的话,在dfs回溯之后把边加入
求点的序列是在每次dfs函数结束之前把点加入

总的来说欧拉回路是一个比较傻逼的东西,但我之前居然不会。。。看来还是要快点把书上的东西过一遍了。

2.哈密顿图

哈密顿回路:从图上一点出发,经过所有的点必须且只能一次,最终回到起点的路径
哈密顿图:存在哈密顿回路的图

哈密顿图的判断是一个典型的NPC问题,但有两个判定条件被证明是对的:
  1. 充分条件:设图G是具有n个顶点的无向连通图,如果G中任意两个不同顶点的度数之和大于等于n,则G具有哈密顿回路,即G是哈密顿图。
  2. 必要条件:设图G是哈密顿图,则对于该图的任意一个非空点集S,W(G-S)<=|S|都成立,其中G-S为G去掉S中的点和与它们相连的边,W(G-S)就是剩下的东西的连通分量的个数,|S|就是S中点的个数。
在图G满足第一个条件的情况下,有构造哈密顿回路的算法:
先找两个相邻的点,然后两边一直伸长到不能在伸为止,就得到一条路径,如果此时路径两端点之间也存在边的话就是一个回路
如果不相连的话,根据第一个条件和鸽巢原理,可以知道两端点间必存在两个相邻的点,分别与两端点有边相连,那么重新安排一下还是可以得到一个回路
得到一个回路之后,如果回路上的点为N则已经求出哈密顿回路,否则在这个回路中找出有边与剩余的点相连的点,然后在这个点处随便一边把回路断开,让这个点继续往外扩展,这就回到了第一步,一直重复到回路上的点数为N为止。

模板代码:(由于时间复杂度是n^2的,所以用邻接矩阵来存储也无所谓了,判断两点是否相连时还更快一些)

int ans[maxn];
bool con[maxn][maxn];
bool vis[maxn];

inline void reverse(int ans[maxn],int s,int t)
{
    int temp;
    while(s<t)
    {
        swap(ans[s],ans[t]);
        s++;t--;
    }
}

inline int extend(int ans[maxn],int &cur)
{
    while(1)
    {
        bool flag=1;
        rep(i,1,n)
        {
            if(con[cur][i] && !vis[i])
            {
                ans[cur++]=i;
                vis[i]=1;
                flag=0;
                break;
            }
        }
        if(flag)break;
    }
    return ans[cur-1];
}

void Hamilton(int ans[maxn],bool con[maxn][maxn],int n)
{
    int s=1,t;
    int ansi=2;
    int i,j,w,temp;
    clr(vis,0);
    rep(i,1,n)if(con[s][i])break;
    t=i;
    vis[s]=vis[t]=1;
    ans[0]=s;ans[1]=t;
    while(1)
    {
        t=extend(ans,ansi);
        reverse(ans,0,ansi-1);
        swap(s,t);
        t=extend(ans,ansi);
        if(!con[s][t])
        {
            w=ansi-1;
            for(int i=1;i<w;i++)
            {
                if(con[ans[i]][t] && con[ans[i+1]][s])
                {
                    reverse(ans,i+1,w);
                    t=ans[w];
                    break;
                }
            }
        }
        if(ansi==n)return;
        rep(i,1,n)if(!vis[i])
        {
            for(j=0;j<ansi;j++)if(con[ans[j]][i])break;
            if(j<ansi)
            {
                vis[i]=1;
                reverse(ans,j,ansi-1);
                reverse(ans,0,j-1);
                ans[ansi++]=i;
                break;
            }
        }
    }
}


你可能感兴趣的:(图的可行遍性)