题意:
要到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;
}