POJ3311 Hie with the Pie(DP:TSP问题)

题意:

要到n个城市送披萨,每个城市只能经过一次,最后要回到原点,求最短路径。

要点:

经典的TSP问题,从0出发找到一条回路回到0,这就意味着可以回溯到前面的城市重新出发,这样就比较难。基本上可以想到先用Floyd算法求出每两个点的最短路径,然后就是DP,定义一个d[i][s]数组,表示当前在第i个城市,已经经过了s集合中的城市(包括i),s用二进制存储。

这样可以写出状态转移方程:d[i][s] = min(d[i][s], map[j][i] + d[j][s ^ (1 << (i - 1))]),在没经过城市I的状态中,寻找合适的中间点J使得距离更短,和Floyd一样。注意这题是有向图,i到j和j到i不一样。


15591242 Seasonal 3311 Accepted 204K 0MS C++ 1118B 2016-06-05 15:23:23
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<algorithm>
using namespace std;
const int inf = 100000000;
int map[15][15],d[15][1<<11];//d(i,s)表示在第i个城已经访问s中的所有城市后返回0的最小路径长度
int n;

void floyd()//先用Floyd算法算出所有最短路径
{
	for (int k = 0; k <= n; k++)
		for (int i = 0; i <= n; i++)
			for (int j = 0; j <= n; j++)
				if (map[i][j] > map[i][k] + map[k][j])
					map[i][j] = map[i][k] + map[k][j];
}
void dp()
{
	int i, j, s;
	for (s = 0; s <= (1 << n)-1; s++)
		for (i = 1; i <= n; i++)
			if (s & 1 << (i - 1))//说明已经经过城市i
			{
				if (s == 1 << (i - 1))//此状态只要再经过i城市,所以直接从i回到0即可
					d[i][s] = map[0][i];
				else
				{
					d[i][s] = inf;
					for (j = 1; j <= n; j++)
						if (s & (1 << (j - 1)) && j != i)//对其他不是i的城市进行状态转移
							d[i][s] = min(d[i][s], map[j][i] + d[j][s ^ (1 << (i - 1))]);//注意题目里i到j和j到i可能不一样,所以这里必须为j到i
					//在没经过城市i的状态中,寻找合适的中间点j使得距离更短,和floyd一样
				}
			}
	int ans = inf;
	for (i = 1; i <= n; i++)//最后还要从i回到0
		ans = min(d[i][(1 << n)-1] + map[i][0], ans);
	printf("%d\n", ans);
}
int main()
{
	int i, j;
	while (scanf("%d", &n)&&n)
	{
		for (i = 0; i <= n; i++)
			for (j = 0; j <= n; j++)
				scanf("%d", &map[i][j]);
		floyd();
		dp();
	}
	return 0;
}


posted @ 2016-06-05 15:50  seasonal  阅读(81)  评论(0编辑  收藏  举报