新年好
感言:对于dfs本来就不太会的我,加之几个板子背的乱七八糟的人而言还是蛮难的,来日方长,继续加油。
知识点:最短路 + DFS + 离散化
解题思路:
本题不是一般的最短路问题,求解的是拜访完所有亲戚这一路上需要走的最短路径。如果没有想到暴力枚举所有亲戚家的遍历次序的话,可能就GG了。
本题的一大约束就是,总共就5个亲戚,加之题目是稀疏图,所以可以通过堆优化版dijkstra算法求加上自己家在内的6个点到其余点的最短路
最短路求出来之后,我们只需要以1为访问的起点,然后求出6的全排列,按照全排列的顺序访问一遍即可求出每次路径的代价,最后求最小即可。
DFS实现的功能即为求全排列的次序,但是要注意的是因为点是经过离散化后映射到数组中的,所以需要反映射回来用
#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
typedef pair<int,int> PII;
const int N = 1e6 + 10, INF = 0X3f3f3f3f;
int n, m;
int h[N],e[N],ne[N],w[N],idx;
//f[]数组记录的是所有亲戚点的下标
int f[N];
//dist[]数组记录的是从家,亲戚家到所有其他点的最短距离
int dist[10][N];
//re[]数组记录的是亲戚id的离散化值
int re[N];
//way[]数组记录的是全排列的次序
int way[N];
//v[]数组记录的是当前节点是否访问过
bool v[N];
int Max =INF;
void add(int a,int b,int c)
{
e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx ++;
}
//dfs函数实现的是算出从1到所有亲戚家路径顺序的全排列
void dfs(int x)
{
if(x == 5)
{
int res = 0;
int be = 1;
//依次遍历全排列的5个位置
for(int i = 1; i <= 5; i ++ )
{
res += dist[re[be]][way[i]];
be = way[i];
}
Max = min(res,Max);
return ;
}
//此处的i代表的是剩余5个亲戚家的位置的索引的索引
for(int i = 1; i < 6; i ++ )
{
//求出亲戚家的索引
int j = f[i];
if(!v[j])
{
v[j] = true;
way[x + 1] = j;
dfs(x+1);
v[j] = false;
way[x + 1] = -1;
}
}
}
void dijkstra(int k)
{
memset(v, 0, sizeof v);
priority_queue<PII,vector<PII>,greater<PII>> q;
q.push({0,f[k]});
dist[k][f[k]] = 0;
while(q.size())
{
auto t = q.top();
int dis = t.first, ver = t.second;
q.pop();
if(v[ver]) continue;
v[ver] = true;
for(int i = h[ver]; i != -1; i = ne[i])
{
int j = e[i];
if(dist[k][j] > dis + w[i])
{
dist[k][j] = dis + w[i];
q.push({dist[k][j],j});
}
}
}
}
int main()
{
memset(h, -1, sizeof h);
memset(dist, 0x3f, sizeof dist);
scanf("%d%d",&n,&m);
f[0] = 1;
re[1] = 0;
for(int i = 1; i < 6; i ++ )
{
scanf("%d",&f[i]);
re[f[i]] = i;
}
for(int i = 0; i < m; i ++ )
{
int a, b, c;
scanf("%d%d%d",&a,&b,&c);
add(a,b,c);
add(b,a,c);
}
//计算所有相关点到其他点的最短路
for(int i = 0; i < 6; i ++ )
dijkstra(i);
memset(v, 0, sizeof v);
way[0] = 1;
dfs(0);
cout<<Max;
return 0;
}