POJ 3311 Hie with the Pie

POJ 3311 Hie with the Pie

题目链接:POJ 3311 Hie with the Pie

算法标签: 动态规划(DP)状态压缩

题目

题意翻译

有N个城市(1~N且N<=10)要去送匹萨。一个PIZZA店(编号为0),求一条回路,从0出发,又回到0,而且距离最短(可重复走)。

题目描述

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.

输入输出样例

输入 #1

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

输出 #1

8

题解:

状压DP(TSP问题) + Floyd最短路

这道题是一道简单的DP解TSP问题,那么我们先说一下什么是TSP问题

​ 旅行商问题,即TSP问题(Travelling Salesman Problem)又译为旅行推销员问题、货郎担问题,是数学领域中著名问题之一。

问题如下:

​ 假设有一个旅行商人要拜访n个城市,他必须选择所要走的路径,路径的限制是每个城市只能拜访一次,而且最后要回到原来出发的城市。路径的选择目标是要求得的路径路程为所有路径之中的最小值。

​ 迄今为止,这类问题中没有一个找到有效算法。认为这类问题的大型实例不能用精确算法求解,必须寻求这类问题的有效的近似算法。而我们所说的都是简化的TSP问题,也就是不一定每个城市只能访问一次或者路径上可以重复等等。

那么对于这道题就是一个简化的TSP问题,首先对于(N le 10)的数据的最短路我们可以先用Floyd求出。对于这道题来说,一个城市是否被访问过标记成一个'0/1',那么我们就把所有城市是否访问过压缩成一个(S)(披萨店不一定在所压缩的状态中)。

之后对于这这道题的DP过程:

状态:(f[s][i])表示到达过(s)集合的所有点,并且当前位置为(i)的最短距离。

转移:(f[s][i] = min(f[s][i], f[s^(1<<(i-1))][j] + dis[j][i]))

​ 特别注意!!!题目中没有说过(dis[i][j] = dis[j][i]),所以不可以把两者看做相等。

答案:(ans = min(ans,f[(1<<n)-1][i]+dis[i][0])~~~~~~~[1 le i le n])

具体注释见代码。

AC代码

//#include <bits/stdc++.h>
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>

using namespace std;

const int inf = 0x3f3f3f3f;

int n, dis[15][15], dp[1 << 15][15];

void readin()
{
	for (int i = 0; i <= n; i ++ )
	{
		for (int j = 0; j <= n; j ++ )
		{
			scanf("%d", &dis[i][j]);
			//由于包含披萨店(0点),所以要从0~n读入
		}
	}
}
void 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]);
}
int main()
{
	while (scanf("%d", &n) && n)
	{
		readin();  // 读入
		floyd();  //Floyd最短路
		int tot = (1 << n) - 1;  //状态上界
		for (int s = 0; s <= tot; s ++ )
		{
			for (int i = 1; i <= n; i ++ )
			{
				if (s & (1 << (i - 1)))  //在S的集合中已经包含i
				{
					if (s == (1 << (i - 1)))  //若S中只包含i,那么证明dp[s][i]就是披萨店到i的距离
						dp[s][i] = dis[0][i];
					else
					{
						dp[s][i] = inf;
						for (int j = 1; j <= n; j ++ )
						{
							if (s & (1 << (j - 1)) && i != j)
								dp[s][i] = min(dp[s][i], dp[s ^ (1 << (i - 1))][j] + dis[j][i]);
								//在没经过i的状态中(S集合不包含i),寻找是否有中间点j使距离更短
								//注意这里dis[i][j] != dis[j][i]
						}
					}
				}
			}
		}
		int ans = inf;
		for (int i = 1; i <= n; i ++ )
		{
			ans = min(ans, dp[tot][i] + dis[i][0]);
			//最终统计ans的最小值就是答案
		}
		printf("%d
", ans);
	}
	return 0;
}
原文地址:https://www.cnblogs.com/littleseven777/p/11841556.html