You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

110 lines
4.5 KiB

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> PII;
const int N = 50010; // 车站数目
const int M = N << 1 << 1; // 公路数目一般来说N个节点通常是2*N条边如果是无向图再乘2
const int INF = 0x3f3f3f3f;
int n, m; // 车站数目,公路数目
// 存图
int h[N], e[M], w[M], ne[M], idx;
void add(int a, int b, int c) {
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}
int dis[6][N];
int id[6]; // 0号索引佳佳的家,其它5个亲戚分别下标为1~5值为所在的车站编号
/*
1、用在Dijkstra中判断量不是出过队列多次调用Dijkstra需要在函数体内进行状态重置 
2、在dfs求全排列时需要清空后记录在此路线上此 亲戚号 是不是走过了
*/
bool st[N];
/*
S:出发车站编号
dis[]:是全局变量dis[6][N]的某一个二维,其实是一个一维数组
C++的特点:如果数组做参数传递的话,将直接修改原地址的数据
此数组传值方式可以让我们深入理解C++的二维数组本质:就是多个一维数组,给数组头就可以顺序找到其它相关数据
计算的结果获取到S出发到其它各个站点的最短距离记录到dis[S][站点号]中
*/
void dijkstra(int S, int dis[]) {
dis[S] = 0;
memset(st, false, sizeof st);
priority_queue<PII, vector<PII>, greater<PII>> q;
q.push({0, S});
while (q.size()) {
auto t = q.top();
q.pop();
int u = t.second;
if (st[u]) continue;
st[u] = true;
for (int i = h[u]; ~i; i = ne[i]) {
int v = e[i];
if (dis[v] > dis[u] + w[i]) {
dis[v] = dis[u] + w[i];
q.push({dis[v], v});
}
}
}
}
int ans = INF; // 预求最小先设最大
// u:第几个亲戚
// pre:前序站点
// sum:按此路径走的总距离和
void dfs(int u, int pre, int sum) {
if (u == 6) { // 如果安排完所有亲戚的拜访顺序,就是得到了一组解,尝试更新最小值
ans = min(ans, sum);
return;
}
for (int i = 1; i <= 5; i++) // 在当前位置上,枚举每个可能出现在亲戚站点
if (!st[i]) { // 如果这个亲戚没走过
st[i] = true; // 走它
// 本位置填充完下一个位置需要传递前序是i,走过的路径和是sum+dis[pre][id[i]].因为提前打好表了,所以肯定是最小值,直接用就行了 
// 需要注意的是一维是 6的上限也就是 佳佳家+五个亲戚 ,而不是 车站号(佳佳家+五个亲戚) !因为这样的话数据就很大数组开起来麻烦可能会MLE
// 要注意学习使用小的数据标号进行事情描述的思想
dfs(u + 1, i, sum + dis[pre][id[i]]);
st[i] = false; // 回溯
}
}
int main() {
scanf("%d %d", &n, &m); // 车站数目和公路数目
id[0] = 1; // 佳佳是0id[0]=1,表示佳佳家在1号车站
for (int i = 1; i <= 5; i++) scanf("%d", &id[i]); // 五个亲戚所在车站编号,比如id[1]=2,描述1号亲戚在2号车站
// 建图完成后,图中的节点其实是 车站的站点编号,而不是亲戚编号
memset(h, -1, sizeof h); // 为了存图,需要初始化邻接表
while (m--) { // 建图
int a, b, c;
scanf("%d %d %d", &a, &b, &c); // a号车站到b号车站需要走的时间是c
add(a, b, c), add(b, a, c); // 无向图,双向建边
}
// 计算从某个亲戚所在的车站出发,到达其它几个点的最短路径
// 因为这样会产生多组最短距离,需要一个二维的数组进行存储
memset(dis, 0x3f, sizeof dis);
for (int i = 0; i < 6; i++) dijkstra(id[i], dis[i]);
// 枚举每个亲戚所在的车站站点多次Dijkstra,分别计算出以id[i]这个车站出发,到达其它点的最短距离,相当于打表
// 将结果距离保存到给定的二维数组dis的第二维中去第一维是指从哪个车站点出发的意思
// dfs还要用这个st数组做其它用途所以需要再次的清空
memset(st, 0, sizeof st);
// 1准备走第一家亲戚(具体是a,b,c,d,e哪一家随意都可以)
// 0前序是佳佳自己家,他自己家的序号是0号
// 0已经走过的最短距离和是0
dfs(1, 0, 0);
//  输出结果
printf("%d\n", ans);
return 0;
}