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

61 篇文章 0 订阅
40 篇文章 1 订阅

Description
题意是有n个城市(1~n)和一个披萨店(0),要求一条回路,从0出发,又回到0,而且距离最短
Input
多组输入,每组用例第一行为一个整数n表示成熟数量,之后为一个(n+1)*(n+1)矩阵表示n个城市和披萨店之间的距离矩阵,以n=0结束输入
Output
对于每组用例,输出最短距离
Sample Input
3
0 1 10 10
1 0 1 2
10 1 0 10
10 2 10 0
0
Sample Output
8
Solution
TSP(旅行商)问题,首先不难想到用FLOYD先求出任意2点的距离dis[i][j],接着枚举所有状态,用11位二进制表示10个城市和披萨店,1表示经过,0表示没有经过 定义状态dp[state][j]表示在state状态下,到达城市I的最优值,接着状态转移方程:dp[state][i] = min{dp[s^(1<<(i-1)][k]) + dis[k][i],dp[state][i])},(state^(1<<(i-1))表示未到达城市i的所有状态,1<=k<=n)。对于全1的状态,即state=(1<< n)-1则表示经过所有城市的状态,最终还需要回到披萨店0,那么最终答案就是min{dp[state][i]+dis[i][0]}
Code

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define INF 1<<29
int dp[(1<<11)+5][11];
int main()
{
    int n;
    while(scanf("%d",&n),n)
    {
        int dis[11][11];
        for(int i=0;i<=n;i++)
            for(int j=0;j<=n;j++)
                scanf("%d",&dis[i][j]);
        //Floyd求最短路 
        for(int k=0;k<=n;k++)
            for(int i=0;i<=n;i++)
                for(int j=0;j<=n;j++)
                    dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
        memset(dp,0,sizeof(dp));//初始化 
        for(int state=0;state<(1<<n);state++)//枚举所有状态 
            for(int i=1;i<=n;i++)//枚举所有城市 
                if(state&(1<<(i-1)))//去过i城市 
                {
                    if(state==(1<<(i-1)))//只去过i城市显然此状态最短路即为披萨店到i城市最短路 
                        dp[state][i]=dis[0][i];
                    else//还去过其他城市 
                    {
                        dp[state][i]=INF;//初始化 
                        //从此状态经过的其他城市中选取合适的中间点j城市使得总距离最小 
                        for(int j=1;j<=n;j++)
                            if(i!=j&&state&(1<<(j-1)))
                                dp[state][i]=min(dp[state][i],dp[state^(1<<(i-1))][j]+dis[j][i]);//更新最短总距离 
                    }
                }
        //从经过所有城市的状态中选取最短总距离 
        int ans=dp[(1<<n)-1][1]+dis[1][0];
        for(int i=2;i<=n;i++)
            ans=min(ans,dp[(1<<n)-1][i]+dis[i][0]);
        printf("%d\n",ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值