题干:
重庆城里有 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≤
1
0
5
10^5
105
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
思路:
首先分析题干,要我们求一个最短路,必须经过a,b,c,d,e(顺序可以变更),例:1->a->…->b->c->d->…->e。所以我们要枚举这五个点的顺序,然后对每种顺序跑五次最短路(别忘了从1开始,所以要跑五次)(因为最短路会保存从起点到所有点的最短路数组)。
但是先枚举在跑最短路复杂度是
o
(
5
!
∗
5
∗
k
∗
m
)
o(5!*5*k*m)
o(5!∗5∗k∗m)即
k
∗
6
∗
1
0
7
k*6*10^7
k∗6∗107,k稍微大一点就会破1e8,容易超时(spfa已经是最短路最快的了…)。
但是我们发现
5
∗
k
∗
m
5*k*m
5∗k∗m其实不需要做多次,在枚举顺序的时候最短路重复做了;所以我们可以先跑最短路再枚举顺序,这样复杂度就是
o
(
5
!
+
5
∗
k
∗
m
)
o(5!+5*k*m)
o(5!+5∗k∗m)。
要注意的细节在代码中。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
#include <vector>
using namespace std;
const int M = 50010;
struct stu{
int next;
int cost;
};
vector<stu> map[M];
int n,m,scource[6],dis[6][M];
bool vis[M];
void add(int u,int v,int c){
stu t;
t.next=v;
t.cost=c;
map[u].push_back(t);
}
void spfa(int sc,int dis[]){
memset(dis,0x3f,4*M);
queue<int> q;
vis[sc]=true;
dis[sc]=0;
q.push(sc);
while(!q.empty()){
int u=q.front();
q.pop();
vis[u]=false;
//回收当前点的标记
for(int i=0;i<map[u].size();i++){
int v=map[u][i].next;
if(dis[v]>dis[u]+map[u][i].cost){
dis[v]=dis[u]+map[u][i].cost;
if(!vis[v]){
q.push(v);
vis[v]=true;
}
}
}
}
}
int dfs(int now,int sta,int distance){
if(now>5)
return distance;
//当前最小值
int ans=0x3f3f3f3f;
//枚举顺序
for(int i=1;i<=5;i++){
if(!vis[i]){
int next=scource[i];
vis[i]=true;
ans=min(ans,dfs(now+1,i,dis[sta][next]+distance));
vis[i]=false;
}
}
return ans;
}
int main(){
int a,b,c;
scanf("%d%d",&n,&m);
for(int i=1;i<6;i++)
scanf("%d",&scource[i]);
scource[0]=1;
for(int i=0;i<m;i++){
scanf("%d%d%d",&a,&b,&c);
add(a,b,c);
add(b,a,c);
}
for(int i=0;i<6;i++){
memset(vis,false,sizeof(vis));
//其实这里可以不用初始化,因为spfa会回收vis数组。。
spfa(scource[i],dis[i]);
}
memset(vis,false,sizeof(vis));
//这里其实也不用。。。
printf("%d\n",dfs(1,0,0));
//直接dfs遍历顺序
return 0;
}