POJ 3311 Hie with the Pie(Floyd+状态压缩DP)

题是看了这位的博客之后理解的,只不过我是又加了点简单的注释。

链接:http://blog.csdn.net/chinaczy/article/details/5890768

我还加了一些注释代码,对于新手的我,看起来可能更方便些吧,顺便说下快捷键

先选中要操作的行,ctrl+shift+c 是注释 ctrl+shift+x是解注释(cb的快捷键)

/*
Floyd + 状态压缩DP
题意是有N个城市(1~N)和一个PIZZA店(0),要求一条回路,从0出发,又回到0,而且距离最短
也就是TSP(旅行商)问题,首先不难想到用FLOYD先求出任意2点的距离dis[i][j]
接着枚举所有状态,用11位二进制表示10个城市和pizza店,1表示经过,0表示没有经过
定义状态DP(S,i)表示在S状态下,到达城市I的最优值
接着状态转移方程:DP(S,i) = min{DP(S^(1<<i-1),k) + dis[k][j],DP(S,i)},其中S^(1<<i-1)表示未到达城市i的所有状态,1<=k<=n
对于全1的状态,即S = (1<<n)-1则表示经过所有城市的状态,最终还需要回到PIZZA店0
那么最终答案就是min{DP(S,i) + dis[i][0]}

TSP是NP困难问题,所以地图必须要小,本题是10,所以也可以根据这个想到floyd,(时间复杂度n^3)和状压dp (用二进制存10位,且只有2个状态,走过,没走过)
*/
#include <map>
#include <set>
#include <list>
#include <cmath>
#include <queue>
#include <stack>
#include <vector>
#include <cstdio>
#include <string>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define INF 100000000
using namespace std;
int dis[12][12];
int dp[1<<11][12];
int n,ans,_min;
int main()
{
#ifndef ONLINE_JUDGE
    freopen("C:\Users\Zmy\Desktop\in.txt","r",stdin);//这应该修改下重定向的文件路径
//    freopen("C:\Users\Zmy\Desktop\out.txt","w",stdout);
#endif // ONLINE_JUDGE
    while(scanf("%d",&n) && n)
    {
        /**< 输入 */
        for(int i = 0; i <= n; ++i)
            for(int j = 0; j <= n; ++j)
                scanf("%d",&dis[i][j]);

//        puts("before");
//        for(int i = 0; i <= n; ++i)
//        {
//            for(int j = 0; j <= n; ++j)
//                printf("%3d ",dis[i][j]);
//            puts("");
//        }

        /**< 这段用弗洛伊德求出了从i点到各个j点的最短距离 存在了dis中,即更新了dis */
        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];

//        puts("after");
//        for(int i = 0; i <= n; ++i)
//        {
//            for(int j = 0; j <= n; ++j)
//                printf("%3d ",dis[i][j]);
//            puts("");
//        }



        for(int S = 0; S < (1<<n); ++S) //枚举所有状态,用位运算表示
            for(int i = 1; i <= n; ++i)
            {
                if(S & (1<<(i-1)))//状态S中已经过城市i
                {

                    /**< 注意:DP的边界是这句话,好牛逼!! */
                    if(S == (1<<(i-1)))	dp[S][i] = dis[0][i];//状态S只经过城市I,最优解自然是从0出发到i的dis,这也是DP的边界

                    //如果S有经过多个城市
                    else
                    {
                        dp[S][i] = INF;

                        //枚举不是城市I的其他城市
                        for(int j = 1; j <= n; ++j)
                        {
                            //在没经过城市I的状态中,寻找合适的中间点J使得距离更短,和FLOYD一样
                            if(S & (1<<(j-1)) && j != i)
                                dp[S][i] = min(dp[S^(1<<(i-1))][j] + dis[j][i],dp[S][i]);
                        }


                    }
                }
            }
        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
",ans);
    }
    return 0;
}

  

原文地址:https://www.cnblogs.com/s1124yy/p/5520987.html