#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 100;
const int NoEdge = 0x3f3f3f3f; //用最大值代表两城市间无边
int n; //城市个数
int m; //邻接矩阵graph边数
int x[maxn]; //当前解,表示从解空间树根到当前结点代表的所经过的城市
int bestx[maxn]; //当前最优解,已搜索的部分解空间树中费用最小周游路线所对应的经过的城市
int graph[maxn][maxn]; //城市之间的临界矩阵,当graph[i][j] = NoEdge时,表示城市i和j无边
int cc; //当前费用
int bestc = NoEdge; //最小费用
//交换
void Swap(int *a, int *b)
{
int t = *a;
*a = *b;
*b = t;
}
void Backtrack(int i)
{
if(i == n)//当i=n时,当前扩展结点是排列树的叶结点的父结点
{
//算法是否存在从城市x[n-1]到城市x[n]的边和从城市x[n]到城市x[1]的边
if(graph[x[n - 1]][x[n]] != NoEdge && graph[x[n]][1] != NoEdge
//判断这条回路的费用是否优于已找到的当前最优回路费用
&& (cc + graph[x[n - 1]][x[n]] + graph[x[n]][1] < bestc || bestc == NoEdge))
{
//更新当前当前最优解bestx
for(int j = 1; j <= n; j++)
bestx[j] = x[j];
//更新当前最优值bestc
bestc = cc + graph[x[n - 1]][x[n]] + graph[x[n]][1];
}
return;
}
else
{
for(int j = i; j <= n; j++)
{
//是否可进入x[j]子树?
if(graph[x[i - 1]][x[j]] != NoEdge && //检测是否存在一条从城市x[i-1]到城市x[j]的边
(cc + graph[x[i - 1]][x[j]] < bestc || bestc == NoEdge)
/*判断从根结点到当前搜索结点处的部分周游
路线的费用是否小于当前找到的最小费用周游路线*/
)
//如果满足约束函数和限界函数,则搜索以当前搜索结点为根的子树
Swap(&x[i], &x[j]);
cc += graph[x[i - 1]][x[i]];
Backtrack(i + 1);
//回溯还原
cc -= graph[x[i - 1]][x[i]];
Swap(&x[i], &x[j]);
}
}
}
int main()
{
fill(graph[0], graph[0] + maxn * maxn, NoEdge);//初始化邻接矩阵为NoEdge
cin >> n >> m;
while(m--)
{
int a, b, c;
cin >> a >> b >> c;
graph[a][b] = graph[b][a] = c;
}
//注意:调用函数回溯搜索前要将数组x初始化为(1,2,…,n)
for(int i = 1; i <= n; i++)
x[i] = i;
Backtrack(2);
cout << "最小花费为:" << bestc << endl;
cout << "路径为:" << endl;
for(int i = 1; i <= n; i++)
cout << bestx[i] << " ";
cout << bestx[1] << endl;
}
/*
测试:
4 6
1 2 30
1 3 6
1 4 4
2 4 10
2 3 5
3 4 20
*/