哈密頓回路


哈密頓回路好多,其實不是很難,但是看了一天了。。看一會睡一會,什么狀態啊。。 
View Code
/*
【題目來源】
http://poj.org/problem?id=2438
【題目分析】
有敵對關系的小朋友,不能坐在一起。最后圍成一個圈,吃飯。。。
將小朋友看成點,有敵對關系的看成沒有邊,最后構成一個回路。
哈密頓回路。 

【小小總結】 
哈密頓回路
充分條件:
無向連通圖中任意2點度數之和大於等於頂點數,則必定存在哈密頓回路。 

思路分析: 
1.任意找兩個相鄰的節點S和T,在它們基礎上擴展出一條盡量長的沒有重復節點的路徑。
也就是說,如果S與節點v相鄰,而且v不在路徑S->T上,則可以把該路徑變成v->S->T,然后v成為新的S。
從S和T分別向兩頭擴展,直到無法擴為止,即所有與S或T相鄰的節點都在路徑S->T上。
2.若S與T相鄰,則路徑S->T形成了一個回路。
3.若S與T不相鄰,可以構造出一個回路。設路徑S->T上有k+2個節點,依次為S、v1、v2……vk和T。
可以證明v1到vk中必定存在vi,滿足vi與T相鄰,且vi+1與S相鄰。(其實vi,vi+1與s t同時相鄰) (怎么證明就不贅述了,反正刷題肯定不會叫你證)
找到了滿足條件的節點vi以后,就可以把原路徑變成S->vi+1->T->vi->S,即形成了一個回路。(自己畫圖就知道了) 
4.現在我們有了一個沒有重復節點的回路。如果它的長度為N,則哈密頓回路就找到了。
如果回路的長度小於N,由於整個圖是連通的,所以在該回路上,一定存在一點與回路以外的點相鄰。
那么從該點處把回路斷開,就變回了一條路徑。再按照步驟1的方法盡量擴展路徑,則一定有新的節點被加進來。(畫圖就知道了) 
接着回到步驟2。 

偽代碼: 
思路清楚后主要是理解好偽代碼,偽代碼一懂代碼就寫出來了。關於下面步驟中為什么要倒置,自己畫畫圖就清楚了。 
s為哈密頓回路起點,t為當前哈密頓回路的終點,ans[]就是哈密頓回路啦,默認不包含0頂點
1.初始化,令s=1,t為任意與s相鄰的點。
2.若ans[]中的元素個數小於n,則從t開始擴展,若可擴展,則把新點v加入ans[],並令t=v,繼續擴展到無法擴展。
3.將ans[]倒置,s,t互換,從t(原來的s)開始擴展,若可擴展,則把新點v加入ans[],並令t=v,繼續擴展到無法擴展。
4.此時s,t兩頭都無法擴展了,若s,t相連,則繼續步驟5。若st不相連,則遍歷ans[],必定會有2點,ans[i]與t相連,ans[i+1]與s相連,
將ans[i+1]到t倒置,t=ans[i+1](未倒置前的)
5.st相連,此時為一個環。若ans[]個數等於n,算法結束,ans[]為哈密頓回路,如需要再添加一個起點。
若ans[]個數小於n,遍歷ans[],尋找ans[i],使得ans[i]與ans[]外一點j相連,倒置ans[]中s到ans[i-1]部分,令s = ans[i-1],
再倒置ans[]中ans[i]到t的部分,j加入ans[],t = j.繼續步驟2 

下面去掉main函數,就是求解哈密頓回路的模版了。 
*/
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;

#define Max 500

int map[Max][Max];
int ans[Max];
bool vis[Max];

//ans數組的index 
int index;
int n, m;
int s, t;

void init()
{
    for (int i = 0; i < Max; ++i)
        for (int j = 0; j < Max; ++j)
            if (i == j)
                map[i][j] = 0;
            else
                map[i][j] = 1;
    
    memset(ans, 0, sizeof(ans));
    memset(vis, 0 , sizeof(vis));
    
    index = 0;
}

void reverse(int a, int b)
{
    while (a < b)
    {
        swap(ans[a], ans[b]);
        
        a++;
        
        b--;
    }
}

void expand()
{
    while (true)
    {
        int i;
        
        for (i = 1; i <= n; ++i) 
        {
            if (!vis[i] && map[i][t])//未被訪問且與t相連 
            {
                ans[index++] = i;
                
                vis[i] = true;
                
                t = i;
                
                break;
            }
        }
        
        if (i > n) break;//無法擴展 
    }
}

void Hamilton()
{
    //初始化s = 1
    s = 1; 
    
    //取任意連接s的點 
    for (int i = 1; i <= n; ++i)
    {
        if (map[i][s])
        {
            t = i;
            
            break;
        }
    }
    
    vis[s] = true;
    
    vis[t] = true;
    
    ans[index++] = s;
    
    ans[index++] = t;
    
    
    while (true)
    {
        //從t向外擴展 
        expand(); 
        
        //t擴展完畢,倒置ans並交換s,t 
        reverse(0, index-1);
        
        swap(s, t);
        
        //從另一頭,t(原來的s)繼續擴展 
        expand();
        
        //若s,t不相連,處理成相連 
        if (!map[s][t])
        {
            //在ans[1]到ans[index-2]中尋找兩個相鄰的且與st同時相連的點(必存在) 因為涉及i+1所以i < index-2 
            for (int i = 1; i < index-2; ++i) 
            {
                if (map[ans[i+1]][s] && map[ans[i]][t])
                {
                    reverse(i+1, index-1);//倒置ans[i+1]到ans[index-1] 
                    
                    t = ans[index-1];//更新t 
                    
                    break;
                }
            }
        }
        
        //若ans元素有n個,說明算法完成 
        if (index == n) return;
        
        //若ans元素不滿n個,ans[]中尋找與未被遍歷過的點相連的點,但這一點必定不是s,t.因為s,t已經遍歷到無法遍歷才能走到這一步 
        for (int j = 1; j <= n; ++j)
        {
            if (!vis[j])
            {
                int i;
                for (i = 1; i < index-1; ++i)//排除st 
                {
                    if (map[ans[i]][j])
                    {
                        s = ans[i-1];
                        
                        t = j;
                        
                        reverse(0, i-1);
                        
                        reverse(i,index-1);
                        
                        ans[index++] = j;
                        
                        vis[j] = true;
                        
                        break;
                    }
                }
                
                if (map[ans[i]][j])break;//記得有2個循環,要break兩次 
            }
        }
        
        //繼續返回,從t擴展。。 
    }
}
    
int main()
{
    while (cin >> n >> m, n||m)
    {
        n *= 2;
        
        init();
        
        int temp1, temp2;
        for (int i = 0; i < m; ++i)
        {
            cin >> temp1 >> temp2;
            
            map[temp1][temp2] = 0;
            
            map[temp2][temp1] = 0;
        }
        
        Hamilton();
        
        cout << ans[0];
        for (int i = 1; i < index; ++i)
            cout << ' ' << ans[i];
        
        cout << endl;
    }
}
        

 


免責聲明!

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



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