题目描述 Description
有一个送外卖的,他手上有n份订单,他要把n份东西,分别送达n个不同的客户的手上。n个不同的客户分别在1~n个编号的城市中。送外卖的从0号城市出发,然后n个城市都要走一次(一个城市可以走多次),最后还要回到0点(他的单位),请问最短时间是多少。现在已知任意两个城市的直接通路的时间。
输入描述 Input Description
第一行一个正整数n (1<=n<=15)
接下来是一个(n+1)*(n+1)的矩阵,矩阵中的数均为不超过10000的正整数。矩阵的i行j列表示第i-1号城市和j-1号城市之间直接通路的时间。当然城市a到城市b的直接通路时间和城市b到城市a的直接通路时间不一定相同,也就是说道路都是单向的。
样例输入 Sample Input
3
0 1 10 10
1 0 1 2
10 1 0 10
10 2 10 0
/*
S表示已经走过的城市,1表示已经走过,0表示未走过(位标记集合)
首先对于原图进行floyd,得到一个最短路的图(16^3,绰绰有余啊)
然后遍历所有的集合
f[x][S]中,x表示完成S集合时,最后走到的是x,然后储存的值就是到这样的状态需要的最小花费。
可能觉得奇怪,假如一开始进行floyd,那么a->b就变成a->c->b,那么在a已入的情况下加入b,那c不是可以直接加入的吗?
不要慌,a->c->b,就是a—>c,c->b,这两条不会被改变(就算被改变,继续递归下去想吧),就不会丢掉c了。
方程:f[x][S]=min(f[x][S],f[k][S^(1<<(x-1))]+_map[k][x]){其中,k已经在S中}
最后不要忘了加_map[x][0],要回去的233
*/
#include <iostream>
#include <cstdio>
using namespace std;
int n;
int f[16][66000],_map[16][16];
int main()
{
cin>>n;
for(int i=0;i<=n;i++){
for(int j=0;j<=n;j++){
scanf("%d",&_map[i][j]);
}
}
//floyd
for(int k=0;k<=n;k++){
for(int i=0;i<=n;i++){
for(int j=0;j<=n;j++){
if(i!=j&&i!=k&&j!=k){
if(_map[i][k]+_map[k][j]<_map[i][j])
_map[i][j]=_map[i][k]+_map[k][j];
}
}
}
}
int Smax=(1<<n)-1;
for(int S=1;S<=Smax;S++){
for(int i=1;i<=n;i++){
if(S&(1<<(i-1))){
if( (S^(1<<(i-1))) ==0){
f[i][S]=_map[0][i];
}else{
f[i][S]=99999999;
for(int j=1;j<=n;j++){
if(S&(1<<(j-1)) && i!=j){
f[i][S]=min(f[i][S],f[j][S^(1<<(i-1))]+_map[j][i]);
}
}
}
}
}
}
int ans=99999999;
for(int i=1;i<=n;i++){
ans=min(ans,f[i][Smax]+_map[i][0]);
}
cout<<ans;
return 0;
}