Hie with the Pie poj3311——Floyd+状压dp

描述

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 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.

输出

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.

样例

  • Input
    3
    0 1 10 10
    1 0 1 2
    10 1 0 10
    10 2 10 0
    0
  • Output
    8

题解

  • 题意:给定n个点,从0开始走完这n个点然后回到0点,两个点之间的往返距离可能会不同,求最小距离
  • 先用Floyd进行预处理,求出从i点到j点之间的最小距离dis[i][j]。
  • 用dp[sta][i]表示在sta状态下,当前走到的最后一个点是i的最小距离,sta中1表示已经走过该点
  • 首先我们枚举每一个状态,对当前状态sta,我们遍历sta的二进制的每一个为1的位置i(即每一个走过点),如果sta中只有一个1,即sta==(1<<(i-1)),则dp[sta][i]=dis[0][i],否则的话,我们取sta1=sta^(1<<(i-1))表示没有走过i点的上一个状态(因为sta1是必然小于sta的,所以在处理sta的时候sta1是已经处理好的数据)。对于上一个状态sta1,我们遍历其二进制中每一个为1的位置j,则dp[sta][i]=max(dp[sta][i],dp[sta1][j]+dis[j][i]),这里可以理解为是一个松弛的操作。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include<stdio.h>
#include<cstring>
#include<algorithm>
#define INIT(a,b) memset(a,b,sizeof(a))
#define LL long long int
using namespace std;
const int MAX=0x7fffffff;
const int MIN=-0x7fffffff;
const int INF=0x3f3f3f3f;
const int Mod=1e9+7;
const int MaxN=1e7+7;
int main(){
//freopen("1.in","r",stdin);
//freopen("1.out","w",stdout);
int n,dis[15][15];
int dp[3000][20]; //dp[sta][i]表示sta状态下,结尾为i的最小消费
while(~scanf("%d",&n)&&n){
INIT(dp,0);INIT(dis,0);
for(int i=0;i<=n;i++)
for(int j=0;j<=n;j++)
scanf("%d",&dis[i][j]);

//预处理两两点之间的最短距离
for(int i=0;i<=n;i++)
for(int j=0;j<=n;j++)
for(int k=0;k<=n;k++)
dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);


for(int sta=1;sta<(1<<n);sta++)
for(int i=1;i<=n;i++)
dp[sta][i]=INF;
dp[0][0]=0;
for(int sta=1;sta<(1<<n);sta++)//枚举所有状态,1表示这个位置走过
for(int i=1;i<=n;i++)
if(sta&(1<<(i-1))){ //枚举这个状态中的每一个1点,对这个点进行松弛
if(sta==(1<<(i-1))) dp[sta][i]=dis[0][i];
else {
int sta1=sta^(1<<(i-1));//得到i点没走过的上一状态stai
for(int j=1;j<=n;j++){ //枚举sta1中的每一个走过的点
if(sta1&(1<<(j-1)))
dp[sta][i]=min(dp[sta][i],dp[sta1][j]+dis[j][i]);
}
}
}
int ans=MAX;
for(int i=1;i<=n;i++)
ans=min(dp[(1<<n)-1][i]+dis[i][0],ans);
printf("%d\n",ans);
}
return 0;
}
Donate comment here