天天看點

漢密爾頓回路問題

概述

這是自己這學期算法課的實驗作業。下面給出漢密爾頓圖的定義。定義如下:對于連通圖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;
}           

複制

運作結果如下:

漢密爾頓回路問題