POJ 3311 Hie with the Pie(TSP模型+Floy+城市状态进行压缩+枚举城市)

题目链接:https://vjudge.net/contest/374535#problem/G
The Pizazz Pizzeria prides itself in delivering pizzas to its customers as fast as possible. Unfortunately, due to cutbacks, they can afford to hire only one driver to do the deliveries. He will wait for 1 or more (up to 10) orders to be processed before he starts any deliveries. Needless to say, he would like to take the shortest route in delivering these goodies and returning to the pizzeria, even if it means passing the same location(s) or the pizzeria more than once on the way. He has commissioned you to write a program to help him.
Input
Input will consist of multiple test cases. The first line will contain a single integer n indicating the number of orders to deliver, where 1 ≤ n ≤ 10. After this will be n + 1 lines each containing n + 1 integers indicating the times to travel between the pizzeria (numbered 0) and the n locations (numbers 1 to n). The jth value on the ith line indicates the time to go directly from location i to location j without visiting any other locations along the way. Note that there may be quicker ways to go from i to j via other locations, due to different speed limits, traffic lights, etc. Also, the time values may not be symmetric, i.e., the time to go directly from location i to j may not be the same as the time to go directly from location j to i. An input value of n = 0 will terminate input.

Output
For each test case, you should output a single number indicating the minimum time to deliver all of the pizzas and return to the pizzeria.
Sample Input

3
0 1 10 10
1 0 1 2
10 1 0 10
10 2 10 0
0

Sample Output

8

翻译:

一个人要到达1n总共n个地方,给出一个矩阵,表示任意两个点间的距离,求从起点0到达n个点(到达指定的n个地点(1n))再回到起点0的最短时间。

TSP问题模型:
旅行商问题,即TSP问题(Traveling Salesman Problem)又译为旅行推销员问题、货郎担问题,是数学领域中著名问题之一。假设有一个旅行商人要拜访n个城市,他必须选择所要走的路径,路径的限制是每个城市只能拜访一次,而且最后要回到原来出发的城市。路径的选择目标是要求得的路径路程为所有路径之中的最小值。

分析:

  1. 邻接矩阵存图,Floy求任意两点之间的距离
void Foly()
{
    for(int k=0; k<=n; k++)
        for(int i=0; i<=n; i++)
            for(int j=0; j<=n; j++)
                if(mp[i][j]>mp[i][k]+mp[k][j])
                    mp[i][j]=mp[i][k]+mp[k][j];
}
  1. 进行状态压缩,从0出发,到达目标城市一路上经过的所有城市压缩为sta状态
    dp[sta][i]dp[sta][i]:在sta的状态下到达i的最短距离
    [0,1<<n)[0,1<<n)为所有状态的集合
  2. 在所有的状态下,枚举要到达的城市
for(int sta=0; sta<(1<<n); sta++)///枚举所有的状态
        {
            for(int i=1; i<=n; i++) ///所要到达的城市
            {
                if(1<<(i-1)==sta)///每一个城市的状态为1<<(i-1),只经过i城市,目标也为i城市,最短路自然是mp[0][i]
                    dp[sta][i]=mp[0][i];
                else ///sta有经过多个城市
                {
                    dp[sta][i]=inf;
                    for(int k=1; k<=n; k++)
                    {
                        if(i!=k&&sta&(1<<(k-1)))///枚举不是i的其他城市
                            dp[sta][i]=min(dp[sta][i],dp[sta^(1<<(i-1))][k]+mp[k][i]);///当前状态下,不是i的其他城市,类似Floy枚举中转点
                    }
                }
            }
        }

完整代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1500;
const int inf=0x3f3f3f3f;
int mp[15][15];
int n;
int dp[N][15];///dp[sta][i]:在经过某些城市到达i城市的最短路,某些城市状态压缩用sta表示
void Foly()
{
    for(int k=0; k<=n; k++)
        for(int i=0; i<=n; i++)
            for(int j=0; j<=n; j++)
                if(mp[i][j]>mp[i][k]+mp[k][j])
                    mp[i][j]=mp[i][k]+mp[k][j];
}
int main()
{
    while(~scanf("%d",&n)&&n)
    {
        for(int i=0; i<=n; i++)
            for(int j=0; j<=n; j++)
                scanf("%d",&mp[i][j]);
        Foly();///求任意两点之间的最短路
        memset(dp,0,sf(dp));
        for(int sta=0; sta<(1<<n); sta++)///枚举所有的状态
        {
            for(int i=1; i<=n; i++) ///所要到达的城市
            {
                if(1<<(i-1)==sta)///每一个城市的状态为1<<(i-1),只经过i城市,目标也为i城市,最短路自然是mp[0][i]
                    dp[sta][i]=mp[0][i];
                else ///sta有经过多个城市
                {
                    dp[sta][i]=inf;
                    for(int k=1; k<=n; k++)
                    {
                        if(i!=k&&sta&(1<<(k-1)))///枚举不是i的其他城市
                            dp[sta][i]=min(dp[sta][i],dp[sta^(1<<(i-1))][k]+mp[k][i]);///在没经过城市i的状态中,寻找合适的中间点k使得距离更短
                    }
                }
            }
        }
        int mi=dp[(1<<n)-1][1]+mp[1][0];///(1<<n)-1为所有的状态,即到城市1之前,所有的城市都已经到达
        for(int i=2; i<=n; i++)
            mi=min(mi,dp[(1<<n)-1][i]+mp[i][0]);
        printf("%d\n",mi);
    }
    return 0;
}

展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 书香水墨 设计师: CSDN官方博客
应支付0元
点击重新获取
扫码支付

支付成功即可阅读