POJ旅行商问题——解题报告

旅行商问题

总时间限制: 1000ms 内存限制: 65536kB

描述

某国家有n(1<=n<=10)座城市,给定任意两座城市间距离(不超过1000的非负整数)。一个旅行商人希望访问每座城市恰好一次(出发地任选,且最终无需返回出发地)。求最短的路径长度。

输入

第一行输入一个整数n
接下来n行,每行n个数,用空格隔开,以邻接矩阵形式给出城市间距离。该邻接矩阵是对称的,且对角线上全为0

输出

一行,最短路径的长度

样例输入
6
0 16 1 10 12 15
16 0 10 2 10 8
1 10 0 10 5 10
10 2 10 0 9 3
12 10 5 9 0 8
15 8 10 3 8 0
样例输出
19

解题思路

这个问题可以抽象为在 n n n阶无向完全图 K n K_n Kn中,给定每个边加权(长度),然后在该带权图中求一条权和最小的哈密顿通路(只求解最小权和即可)。我的想法是使用深度优先搜索求解,过程中使用了set来储存通路的长度,由于set使用二叉搜索树,可以在 O ( log ⁡ n ) O(\log n) O(logn)时间内完成元素插入,同时自动对元素排序,并且能够在 O ( 1 ) O(1) O(1)时间内获得集合的最大和最小值,以略微节省运算时间。另外使用stack容器来临时储存全局变量sum(长度和)的值。

代码
#include 
using namespace std;
int cities[12][12] = {};
bool visited[12] = {};
//标记去过的城市,如果去过了,则记true
int n;
int sum;
set<int> path;
stack<int> temp;
void dfs(int start)
{
    bool flag = true;
    for (int i = 1; i <= n; i++)
        if (!visited[i])
            flag = false;
    if (flag)
    {
        path.insert(sum);
        return;
    }
    //设置边界
    //如果没到递归边界,继续
    for (int i = 1; i <= n; i++)
    {
        if (visited[i] == false)
        {
            visited[i] = true;
            if (sum > *path.begin() && path.size() != 0)
            {
                visited[i] = false;
                continue;
            }
            temp.push(sum);
            sum += cities[start][i];
            dfs(i);
            sum = temp.top();
            temp.pop();
            visited[i] = false;
        }
    }
}

int main()
{
    scanf("%d", &n);
    sum = 0;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++)
            scanf("%d", &cities[i][j]);
    for (int i = 1; i <= n; i++)
        dfs(i);
    printf("%d\n", *path.begin());
    return 0;
}
其他想法

由于此题中旅行商可以从任意城市出发,我在解题的过程中分别搜索了 n n n个不同起点的解。但可以看出,搜索的不同路径中有大量重和部分,虽然对那些不可能成为最优解的路径进行了剪枝,搜索的效率仍然不高。
于是便有了另外一种思路,即,在图中先搜索出一条最短的哈密顿回路,然后删去这条回路中的最长边,便得到一条哈密顿通路。由最短的哈密顿回路得到的这条哈密顿通路是否是最短的哈密顿回路,我并没有进行严格的证明。

你可能感兴趣的:(POJ旅行商问题——解题报告)