【ダイナミックプランニング】バイナリ思考でTSP(旅行者問題)最短経路とその長さを巧みに解く(C++)

30289 ワード

考え方の参考:https://blog.csdn.net/joekwok/article/details/4749713
ネット上で1周見て、比較的に優雅な旅行者の問題の動態的な計画のC++コードがないことを発見して、そこで1篇のブログを書いて分かち合って、まずこの上を見て参考の構想に言及することを提案して、それから本文のコードを見ます.
マクロ表示の都市個数N,グローバル変数表示の重み付き距離行列distances[N][N]を異なる重み付きマップに対して修正することで異なるTSP(旅行者問題)を解決できるように,上のリンクコードを改良した.このコードではdistances[N][N]は乱数で生成され,必要に応じて変更できる.
メモリの制限のため、本コードは都市の個数N≦le≦15の時に正常に運行します;都市数N>>15の場合、ファイルにデータを格納し、ファイルを読み込むことで実現することを推奨します.
コードの鍵は、都市集合をバイナリ01で表すことであり、例えば、都市1,2,3からなる集合{1,2,3}{1,2,3}{1,2,3}、バイナリ111 111 111 111で表す、すなわち、10進数の7 7 7 7,7,2次元配列dpの7列目である.都市1,3からなる集合{1,3}{1,3}{1,3}については、10進法の5 5 5 5,2次元配列dpの5列目であるバイナリ101 101 101 101 101で表される.
したがって,我々の目標はd p[0][11 1 2]dp[0][111_2]dp[0][1112]、すなわちd p[0][7 10]dp[0][7_{10}]dp[0][710]であり,0 0番都市から出発して都市群{1,2,3}{1,2,3}{1,2,3}{1,2,3}を通過することを表し,動的計画の過程は上記のリンクを参照する.
次のコードの理解では、中の多くのバイナリ数の操作を理解することに重点を置いています.他は理解しにくくありません.
記入法のダイナミックプランニング結果印刷はいずれも再帰的な方法であり、path[i][j]はi番都市からjのバイナリ形式で表示される都市群、i番都市の次の都市の番号を格納する.
#include 
#include 
#include 
#include 
using namespace std;

#define N 15
#define MAX 0x3f3f3f3f

// int distances[N][N] = {{MAX, 3, 6, 7}, {5, MAX, 2, 3}, {6, 4, MAX, 2}, {3, 7, 5, MAX}};

int removeCity(int j, int k) { //  j             k   (k   0)
    return j - (1 << (k-1));
}

int TSP(int dp[][1 << (N-1)], int path[][1 << (N-1)], int distances[][N]) {
    if (N == 1) return 0; //       

    for (int i = 1; i < N; i++) {
        dp[i][0] = distances[i][0]; //         0      
    }

    for (int j = 1; j < (1 << (N-1)); j++) {
        for (int i = 1; i < N; i++) {
            if ((j >> (i-1)) % 2 == 0) { // i     j             
                int min = MAX;
                int next_city = i;
                for (int k = 1; k < N; k++) {
                    if ((j >> (k-1)) % 2 != 0) { // k    j             
                        int temp = distances[i][k] + dp[k][removeCity(j, k)]; //    k    j       
                        if (temp < min) {
                            min = temp;
                            next_city = k;
                        }
                    }
                }
                dp[i][j] = min;
                path[i][j] = next_city;
            }
        }
    }
    
    //       
    int min = MAX;
    int next_city = 0;
    int j = (1 << (N-1)) - 1; //    0              
    for (int k = 1; k < N; k++) {
        int temp = distances[0][k] + dp[k][removeCity(j, k)];
        if (temp < min) {
            min = temp;
            next_city = k;
        }
    }
    dp[0][j] = min;
    path[0][j] = next_city;

    
    /*for (int i = 0; i < N; i++) { //        
        for (int j = 0; j < 1 << (N-1); j++) {
            if (dp[i][j] == 0)
                cout << "\\" << " ";
            else
                cout << dp[i][j] << " ";
        }
        cout << endl;
    }*/

    return dp[0][j];
}

void printPath(int path[][1 << (N-1)], int i, int j) { //      i        j          
    if (j != 0) {
        cout << i << " -> ";
        int next_city = path[i][j];
        printPath(path, next_city, removeCity(j, next_city));
    }
    else {
        cout << i << " -> " << 0;
    }
}

int main() {
    int distances[N][N] = {0};
    int path[N][1 << (N-1)] = {0};
    int dp[N][1 << (N-1)] = {0};
    // 1 << (N-1) == pow(2, N-1)
    //    0,1,2...      
    //    000,001,010,... x  1,        x   

    //      distances[N][N]
    srand((unsigned int) time(NULL));
    for (int i = 0; i < N; i++) {
        for (int j = i; j < N; j++) {
            if (i == j) distances[i][j] = MAX;
            else {
                int temp = rand();
                while (temp == 0) {
                    temp = rand();
                }
                distances[i][j] = distances[j][i] = temp;
            }
        }
    }

    //       
    cout << "    :[" << N << "]" << endl;
    cout << "     :" << endl;
    for (int i = 0; i < N; i++) {
        for (int j = 0; j < N; j++) {
            if (distances[i][j] == MAX) cout << setw(5) << "INF" << " ";
            else cout << setw(5) << distances[i][j] << " ";
        }
        cout << endl;
    }

    //          (   ms)
    clock_t start = clock();
    int d = TSP(dp, path, distances);
    clock_t spent = (double) (clock() - start) / CLOCKS_PER_SEC * 1000;
    cout << "    :" << spent << "ms" << endl;
    cout << "     :" << d << endl;
    cout << "     :";
    printPath(path, 0, (1 << (N-1))-1);

    return 0;
}

コード実行結果:
    :[15]
     :
  INF  3729 13549 16032  5214 16144 28132 31901 18181 27523 31894 28842 30136 32028 13307
 3729   INF 13870 20822  6156 17167 11654 12498  3912 27011 11758  2954 17805 25778  3749
13549 13870   INF 28116  8097 10456 19276  7130 27613 14974 18319 22185 25320 15417 30216
16032 20822 28116   INF 30596  9669  1172 26208 21886 23824 20555 32440 10568 23940 25786
 5214  6156  8097 30596   INF 25671 25415 30145 13972 22340 10217 18764 26840  1542 26170
16144 17167 10456  9669 25671   INF 23768 14337  7018 15538 14276  9598  3189  8273 16100
28132 11654 19276  1172 25415 23768   INF 30561 26938 31215 11396 24667 13566 11725 18858
31901 12498  7130 26208 30145 14337 30561   INF 25576  2459 16751 19612 19143  2594  5038
18181  3912 27613 21886 13972  7018 26938 25576   INF   162 15277 26820 23305  6925 19514
27523 27011 14974 23824 22340 15538 31215  2459   162   INF  1011 24124 24952  7724 24260
31894 11758 18319 20555 10217 14276 11396 16751 15277  1011   INF  6932  1358 23763 30872
28842  2954 22185 32440 18764  9598 24667 19612 26820 24124  6932   INF 20353 22669 11973
30136 17805 25320 10568 26840  3189 13566 19143 23305 24952  1358 20353   INF 18427  6381
32028 25778 15417 23940  1542  8273 11725  2594  6925  7724 23763 22669 18427   INF 16329
13307  3749 30216 25786 26170 16100 18858  5038 19514 24260 30872 11973  6381 16329   INF
    :13ms
     :79328
     :0 -> 1 -> 11 -> 10 -> 9 -> 8 -> 13 -> 6 -> 3 -> 5 -> 12 -> 14 -> 7 -> 2 -> 4 -> 0