POJ3311(TSP问题,状态压缩DP)

来源:互联网 发布:淘宝卖燕窝怎么程序 编辑:程序博客网 时间:2024/05/16 14:07

题目链接:http://poj.org/problem?id=3311

分析:由于题中明确说了两个城市间的直接可达路径(即不经过其它城市结点)不一定是最短路径,所以需要借助邻接矩阵首先求出任意两个城市间的最短距离(因为这里的点可以多次遍历,并没有次数限制,所以才能用floyd的,如果有次数限制x的话,就不能用floyd预处理,而应该用x进制的状态压缩了)。这一步骤使用Floyd最短路径算法即可。然后,在此基础上来求出遍历各个城市后回到出发点的最短路径的距离,即求解TSP问题。

考虑搜索算法:这种解法其实就是计算排列子集树的过程。从0点出发,要求遍历123点后回到0点。以不同的顺序来依次遍历123点就会导出不同的路径(0->1->2->3->00->1->3->2->0等等),总共有3!=6条路径需要考虑,从中选出最短的那条就是所求。搜索解法的时间复杂度为 O(n!) 

思索:仔细观察搜索解法的过程,其实是有很多重复计算的。比如从0点出发,经过12345点后回到0点。那么0->1->2->(345三个点的排列)->00->2->1->(345三个点的排列)->0存在重复计算(345三点的排列)->0路径集上的最短路径。只要我们能够将这些状态保存下来就能够降低一部分复杂度。

状态保存:设集合S为一个点集,从0点出发,经过集合S中的每一个点,再回到出发点。那么怎样表示集合S呢?集合S是所有点所组成的集合的子集,那么得到集合S其实就是子集生成,二进制!
考虑DP:记dp[S][i]0点出发,到达集合S中的每一个点并且i是所遍历的最后一个点的(0点)的最短距离。
状态转移方程:dp[S][i] = min(dp[S][i],dp[S’][j]+a[j][i]) S’表示S的不包含i的子集,a[j][i]表示从ji的最短距离。

边界条件:dp[S][i] = a[0][i]S为只包含点i时的状态。
最终结果:min(dp[(1<<n)-1][j]+a[j][0])
#include<iostream>using namespace std;int dp[65540][20];int a[20][20];int main(){    int n;    while (cin>>n && n)    {        for (int i=0; i<=n; i++)            for (int j=0; j<=n; j++) cin>>a[i][j];        for (int k=0; k<=n; k++)            for (int i=0; i<=n; i++)                for (int j=0; j<=n; j++)                    a[i][j] = min(a[i][j],a[i][k]+a[k][j]);        int m = (1<<n)-1;        for (int S=0; S<=m; S++)            for (int i=1; i<=n; i++)                if (S & (1<<(i-1)))        {            if (S == (1<<(i-1)))                dp[S][i] = a[0][i];            else            {                dp[S][i] = (1<<30);                for (int j=1; j<=n; j++)                {                    if (S & (1<<(j-1)) && j != i)                        dp[S][i] = min(dp[S][i],                                       dp[S^(1<<(i-1))][j]+a[j][i]);                }            }        }        int ans = (1<<31)-1;        for (int j=1; j<=n; j++)        {            ans = min(ans, dp[(1<<n)-1][j]+a[j][0]);        }        cout<<ans<<endl;    }    return 0;}


0 0
原创粉丝点击