poj 3311 Hie with the Pie(状压dp,并不需要floyd)

题意:给你一个有n+1(1<=n<=10)个点的有向完全图,用矩阵的形式给出任意两个不同点之间的距离。(其中从i到j的距离不一定等于从j到i的距离)现在要你求出从0号点出发,走过1到n号点至少一次,然后再回到0号点所花的最小时间。
输入:包含多组实例。每个实例第一个为n,然后是n+1行矩阵,每行矩阵有n+1个数字,第i行第j个数字表示从i-1到j-1号点的距离。当输入n=0时表示输入结束。
输出:最小距离。

这题有两种思路:一种是网上普遍的做法就是先求一遍floyd,求出各个点之间的最短路,然后就转化为了TSP问题去求解。需要注意的就是初始化的问题。
附上代码:

#include<iostream> 
#define INF 100000000 
using namespace std; 
int dis[12][12]; 
int dp[1<<11][12]; 
int n,ans,_min; 
int main() 
{ 
   //freopen("in.txt","r",stdin); 
   while(scanf("%d",&n) && n) 
   { 
       for(int i = 0;i <= n;++i) 
           for(int j = 0;j <= n;++j) 
               scanf("%d",&dis[i][j]); 
       for(int k = 0;k <= n;++k) 
           for(int i = 0;i <= n;++i) 
                for(int j = 0;j <=n;++j) 
                    if(dis[i][k] + dis[k][j]< dis[i][j]) 
                        dis[i][j] = dis[i][k] +dis[k][j]; 

       for(int S = 0;S <= (1<<n)-1;++S)//枚举所有状态,用位运算表示 
           for(int i = 1;i <= n;++i) 
           { 
                if(S & (1<<(i-1)))//状态S中已经过城市i 
                { 
                    if(S ==(1<<(i-1)))   dp[S][i] =dis[0][i];//状态S只经过城市I,最优解自然是从0出发到i的dis,这也是DP的边界
                    else//如果S有经过多个城市 
                    { 
                        dp[S][i] = INF; 
                        for(int j = 1;j <=n;++j) 
                        { 
                            if(S &(1<<(j-1)) && j != i)//枚举不是城市I的其他城市 
                                dp[S][i] =min(dp[S^(1<<(i-1))][j] + dis[j][i],dp[S][i]); 
                            //在没经过城市I的状态中,寻找合适的中间点J使得距离更短
                        } 
                    } 
                } 
            } 
       ans = dp[(1<<n)-1][1] + dis[1][0]; 
       for(int i = 2;i <= n;++i) 
           if(dp[(1<<n)-1][i] + dis[i][0] < ans) 
                ans = dp[(1<<n)-1][i] +dis[i][0]; 
       printf("%d\n",ans); 
   } 
   return 0; 
}

下面主要讲讲我的做法。我们可以从题目中发现这题与典型的TSP最大区别就是这题的顶点是可以经过多次的,而普通的TSP只能经过一次。
所以根据挑战上关于TSP的代码我们可以发现,他在状态转移的时候是加了一个判断的,只有当前节点不在某一状态的时候,才会进行状态转移。那么我们就可以类推,因为顶点经过次数没有限制,所以只需要把判断条件去掉即可。

注意:我下面代码将n++了,就说明数据范围从[0,n]变为了[1,n+1]

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int inf = 0x3f3f3f3f;
int dp[5000][15];
int n;
int e[15][15];
int main() {
    while(~scanf("%d", &n) && n) {
        n++;
        for(int i = 1; i <= n; i++) {
            for(int j = 1; j <= n; j++) {
                int x;
                scanf("%d", &x);
                e[i][j] = x;
            }
        }
            memset(dp, inf, sizeof(dp));
            dp[(1 << n) - 1][1] = 0;
            for(int s = (1 << n) - 1; s >= 0; s--) { //每一种状态
                for(int i = 1; i <= n; i++) { //当前所在城市
                    for(int j = 1; j <= n; j++) { //一种状态的每一位,将要去的城市
                        dp[s][i] = min(dp[s][i], dp[s | 1 << (j - 1)][j] + e[j][i]);
                    }
                }
            }
            printf("%d\n", dp[1][1]);
        }
        return 0;
    }

猜你喜欢

转载自blog.csdn.net/yiqzq/article/details/80539066