1. 程式人生 > >漢密爾頓迴路問題

漢密爾頓迴路問題

概述

這是自己這學期演算法課的實驗作業。下面給出漢密爾頓圖的定義。定義如下:對於連通圖G=(V,E),V1,V2,…,Vn是G 的一條通路,且圖中任意兩個頂點都可達,若 中每個頂點在該通路中出現且僅出現一次,則稱該通路為漢密爾頓通路。若 V1=Vn,則稱該通路為漢密爾頓迴路。

演算法描述

1)初始化最佳路徑陣列best_path,同時初始化臨時路徑陣列path與訪問陣列isvisited,設定最小長度min,設定長度變數length = 0
2)開始對每個頂點進行遍歷尋找最佳路徑,首先堆訪問陣列中對應頂點進行置1,並把當前頂點追加到path,同時利用cur_vertex這個臨時變數儲存當前結點,並開始進行迴圈。
3)找到出cur_vertex之外與之相鄰且並未訪問的一個頂點k,利用tmp儲存這兩點之間的權重,之後檢查是否存在比tmp更小且與cur_vertex相鄰的頂點,如有則更新tmp與訪問的頂點k,之後更新length += tmp,以及更新cur_vertex = k,如果length大於min,則說明改路徑無效,跳出迴圈。
4)重複步驟3遍歷每一個結點。迴圈結束後,對length更新,加上最後一個結點到cur_vertex結點的距離。這是如果min大於legnth,則對min更新,並把path陣列複製到best_path中去。
5)重複步驟2)直至遍歷完每個結點。返回最小長度。

//求漢密爾頓迴路函式 
int Hanmilton(){
    int path[1000] = {0};
    int cur_vertex = 0;     //作為儲存當前結點 
    int length = 0;         //漢密爾頓迴路長度
    int min = 10000;        //最小長度 
    for(int i = 1 ; i < this->Nv+1 ; i++){//對每個頂點為初始點進行比遍歷尋找漢密爾頓迴路 
        length = 0;     //重新設定最端長度為0 
        memset(this->isvisited,0,sizeof
(this->isvisited[0])*(this->Nv+1)); //重新初始化訪問陣列為0 this->isvisited[i] = 1; //標記當前結點為已訪問 path[1] = i; //儲存到臨時路徑陣列的第一個 cur_vertex = i; //儲存當前頂點 for(int j = 2 ; j < this->Nv+1 ; j++){//訪問剩餘的結點 int k = 0; //尋找到第一個未訪問的結點 for
(k = 2 ; k < this->Nv+1 ; k++){ if(this->isvisited[k] == 0){ break; } } int tmp = this->data[cur_vertex][k]; //儲存當前頂點到該結點的路徑長度 for(int m = k+1 ; m < this->Nv+1 ; m++){//向後尋找有沒有路徑更短的節點 if((!this->isvisited[m]) && (tmp > this->data[cur_vertex][m])){ tmp = this->data[cur_vertex][m];//更新當前最短路徑 k = m;//更新第一個未被訪問的結點 } } path[j] = k; //儲存路徑上的結點 this->isvisited[k] = 1; //標記為已訪問 cur_vertex = k; //跟新當前結點 length += tmp; //跟新長度 if(length > min){ //當前長度大於最小長度,則改路徑無效,跳出迴圈 break; } } length += this->data[cur_vertex][i]; if(min > length){ //更新最小長度並儲存最佳路徑 min = length; for(int m = 0 ; m < this->Nv+1 ; m++){ this->best_path[m] = path[m]; } } } //返回最小長度 return min; }

例子

下面的例子是基於如下圖結構:
這裡寫圖片描述
全部程式碼如下:

#include <iostream>
#include <cstring> 
#include <vector>
#include <cstdio>
using namespace std;

/*
    邊與邊長:(起點,終點,長度) 
    1 2 2
    1 3 3
    1 4 2
    1 5 5
    2 3 6
    2 4 8
    2 5 10
    3 4 10
    3 5 15
    4 5 12 
*/ 

class Graph{
    private:
        int** data;     //鄰接矩陣 到sa 拉黑聖誕節,  
        int* isvisited; //訪問陣列 
        int Nv;         //頂點數 
        int Ne;         //邊數
        vector<int> best_path;  //漢密爾頓最佳路徑 
    public:
        //建構函式
        Graph(int nv,int ne){
            this->Nv = nv;
            this->Ne = ne;
            this->data = new int*[nv+1];
            best_path.reserve(nv+1);
            for(int i = 0 ; i < nv+1 ; i++){
                best_path[i] = 0;
            }
            //初始化訪問陣列 
            this->isvisited = new int[nv+1];
            memset(this->isvisited,0,sizeof(this->isvisited[0])*(nv+1));
            //對鄰接矩陣進行初始化 
            for(int i = 0 ; i < nv+1 ; i++){
                data[i] = new int[nv+1];
                memset(data[i],0,sizeof(data[i][0])*(nv+1));
            }
            cout<<"請輸入邊與邊長:"<<endl;
            //對邊進行初始化 
            for(int i = 0 ; i < ne ; i++){
                int v1,v2,weight;
                cin>>v1>>v2>>weight;
                this->data[v1][v2] = this->data[v2][v1] = weight;
            } 
        }

        //求漢密爾頓迴路函式 
        int Hanmilton(){
            int path[1000] = {0};
            int cur_vertex = 0;     //作為儲存當前結點 
            int length = 0;         //漢密爾頓迴路長度
            int min = 10000;        //最小長度 
            for(int i = 1 ; i < this->Nv+1 ; i++){//對每個頂點為初始點進行比遍歷尋找漢密爾頓迴路 
                length = 0;     //重新設定最端長度為0 
                memset(this->isvisited,0,sizeof(this->isvisited[0])*(this->Nv+1));  //重新初始化訪問陣列為0 
                this->isvisited[i] = 1;     //標記當前結點為已訪問 
                path[1] = i;        //儲存到臨時路徑陣列的第一個
                cur_vertex = i;     //儲存當前頂點
                for(int j = 2 ; j < this->Nv+1 ; j++){//訪問剩餘的結點 
                    int k = 0;
                    //尋找到第一個未訪問的結點 
                    for(k = 2 ; k < this->Nv+1 ; k++){
                        if(this->isvisited[k] == 0){
                            break;
                        }
                    }
                    int tmp = this->data[cur_vertex][k];        //儲存當前頂點到該結點的路徑長度 
                    for(int m = k+1 ; m < this->Nv+1 ; m++){//向後尋找有沒有路徑更短的節點 
                        if((!this->isvisited[m]) && (tmp > this->data[cur_vertex][m])){
                            tmp = this->data[cur_vertex][m];//更新當前最短路徑 
                            k = m;//更新第一個未被訪問的結點 
                        }
                    }
                    path[j] = k;    //儲存路徑上的結點
                    this->isvisited[k] = 1; //標記為已訪問 
                    cur_vertex = k;     //跟新當前結點 
                    length += tmp;      //跟新長度 
                    if(length > min){   //當前長度大於最小長度,則改路徑無效,跳出迴圈 
                        break;
                    }
                }
                length += this->data[cur_vertex][i];
                if(min > length){       //更新最小長度並儲存最佳路徑 
                    min = length;
                    for(int m = 0 ; m < this->Nv+1 ; m++){
                        this->best_path[m] = path[m]; 
                    }
                }
            }
            //返回最小長度 
            return min;
        }

        //列印最佳漢密爾頓迴路 
        void Print_Best_Path(){
            cout<<this->best_path[1];
            for(int i = 2 ; i < this->Nv+1 ; i++){
                cout<<" -> "<<this->best_path[i];
            }
            cout<<" -> "<<this->best_path[1];
        }

        //列印鄰接矩陣 
        void Print(){
            for(int i = 1 ; i < this->Nv+1 ; i++){
                for(int j = 1 ; j < this->Nv+1 ; j++){
                    printf("%3d",this->data[i][j]);
                }
                cout<<endl;
            }
        }
};

int main()
{
    cout<<"請輸入頂點數與邊數:"<<endl;
    int nv,ne;
    cin>>nv>>ne;
    Graph graph(nv,ne);
    cout<<"鄰接矩陣為:"<<endl;
    graph.Print();
    cout<<"該圖的漢密爾頓迴路長度為:"<<endl;
    int length = 0;
    length = graph.Hanmilton();
    cout<<length<<endl;
    cout<<"漢密爾頓迴路路徑為:"<<endl;
    graph.Print_Best_Path(); 

    return 0;
}

執行結果如下:
這裡寫圖片描述