一、前言
这道题目的时间复杂度卡的很死,需要在算法设计和细节上仔仔细细的考虑才能通过。非常考验对最短路算法的基础理解和灵活运用,是一道值得细细品味的图论题。
二、题面分析
题目链接:Acwing:新年好
重庆城里有 n 个车站,m 条 双向 公路连接其中的某些车站。
每两个车站最多用一条公路连接,从任何一个车站出发都可以经过一条或者多条公路到达其他车站,但不同的路径需要花费的时间可能不同。
在一条路径上花费的时间等于路径上所有公路需要的时间之和。
佳佳的家在车站 1,他有五个亲戚,分别住在车站 a,b,c,d,e。
过年了,他需要从自己的家出发,拜访每个亲戚(顺序任意),给他们送去节日的祝福。
怎样走,才需要最少的时间?
输入格式
第一行:包含两个整数 n,m,分别表示车站数目和公路数目。
第二行:包含五个整数 a,b,c,d,e,分别表示五个亲戚所在车站编号。
以下 m 行,每行三个整数 x,y,t,表示公路连接的两个车站编号和时间。
输出格式
输出仅一行,包含一个整数 T,表示最少的总时间。
数据范围
1≤n≤50000,
1≤m≤1e5,
1<a,b,c,d,e≤n,
1≤x,y≤n,
1≤t≤100
输入样例:
6 6
2 3 4 5 6
1 2 8
2 3 3
3 4 4
4 5 5
5 6 2
1 6 7
输出样例:
21
分析:第一眼反应以为是Floyd解题,然后看到数据范围直接傻了。然后想到先枚举所有可能的路线再依次求最短路,然后马上想到时间复杂度也不允许。在仔细想想后,我们找到一个突破口----亲戚一共只有5个,只要我们能先预处理好5个亲戚和自己的最短距离,然后再进行DFS遍历,时间复杂度就可以刚刚好达到我们的目的。但是即使思路是对的,在代码实现上也要万分小心。有很多细节都是要注意到。
三、代码详解
//#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define INF 0x3f3f3f3f
#define mem(a, b) memset(a, b, sizeof(a))
using namespace std;
typedef pair<int, int> PII;
const int N = 2e5 + 7;
int n, m;
int p[6]; //简单的离散化处理
int h[N], e[N], ne[N], w[N], id = 1;
int dist[6][N]; //dist[i][j]:编号为i的点到点j的距离
bool ch[N]; //dijkstra查重
bool st[6]; //dfs查重
void add(int a, int b, int c)
{
ne[id] = h[a];
h[a] = id;
e[id] = b;
w[id] = c;
id++;
}
int dfs(int step, int now, int res) //步数、当前点的编号、已经走的距离
{
if (step == 6) //到6就说明走完5步了
{
return res; //返回答案
}
int ans = INF; //定义一个足够大的初始答案
for (int i = 1; i <= 5; i++) //枚举每一个亲戚
{
if (!st[i]) //如果这个点没走过
{
st[i] = true;
ans = min(ans, dfs(step + 1, i, res + dist[now][p[i]])); //找最小值
st[i] = false; //时光回溯
}
}
return ans; //返回最后的答案
}
void dijkstra(int s, int dist[ ]) //放进起点和到起点距离的数组,由于这里数组放进去的是地址,所以会直接修改全局变量
{
mem(ch, 0);
mem(dist, INF);
priority_queue<PII, vector<PII>, greater<PII>> q; //小根堆,不要写成了大根堆
dist[s] = 0; //起点距离为0
q.push({ 0, s }); //放进优先队列
while (q.size())
{
auto te = q.top();
q.pop();
int d = te.first;
int idx = te.second;
if (ch[idx] == 1) //同一个点不被用来更新两次
continue;
ch[idx] = 1;
for (int i = h[idx]; ~i; i = ne[i])
{
int j = e[i];
if (dist[j] > d + w[i])
{
dist[j] = d + w[i];
q.push({ dist[j], j });
}
}
}
}
void solve()
{
mem(dist, INF);
mem(h, -1);
cin >> n >> m;
for (int i = 1; i <= 5; i++)
cin >> p[i];
while (m--)
{
int a, b, c;
cin >> a >> b >> c;
add(a, b, c);
add(b, a, c);
}
p[0] = 1;
for (int i = 0; i <= 5; i++) //5次dijkstra更新,多了一点都会tle掉
dijkstra(p[i], dist[i]);
cout << dfs(1, 0, 0);//DFS跑......
}
int main()
{
std::ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
solve();
return 0;
}