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

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


TSP问题模型：

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]$:在sta的状态下到达i的最短距离
$[0,1<为所有状态的集合
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;
}



08-15 535

07-05 1006

09-17 4584

04-29 760

08-04 12

02-21 2000

07-14 625

09-26 1304

09-11 1653

08-01 1498

08-29 1618

07-22 1187

02-22 1851

06-02 2029

07-23 2576

09-11 2822

08-22 2390

08-23 969

07-31 5355

11-09 1700

04-14 59万+

03-13 15万+

03-04 13万+

03-05 6253

03-08 4万+

03-08 7万+

03-10 13万+

03-12 11万+

03-13 11万+

03-14 1万+

03-18 5780

03-18 9633

03-19 3万+

03-19 8万+

03-20 8672

03-24 3万+

03-25 3万+

05-08 5万+

03-25 9万+

03-29 21万+

03-29 1万+

03-29 10万+

03-30 16万+

05-28 5961

05-28 1万+

04-02 4万+

04-03 3392

04-06 7万+

04-07 5万+

04-09 8万+

04-09 2万+

05-17 8283

04-10 2802

04-11 3万+

04-15 6万+

04-18 4万+

04-20 4万+

04-24 3万+

04-29 6211

04-29 6956

04-30 4861

04-30 9904

04-30 9465

05-16 5万+

05-08 4万+

05-11 3万+

05-12 1万+

05-13 680

05-14 6333

05-14 1887

05-16 1万+

05-20 912

05-18 8281

05-27 1259

05-18 3473

05-19 1万+

05-21 7091

05-21 7124

#### 老码农吐血建议：2020年，低于1w的程序员要注意了...

©️2019 CSDN 皮肤主题: 书香水墨 设计师: CSDN官方博客