题意:
n个点,m条边,a,b,c是节点编号,这个人要先从a到b再到c,
第二行是m条边的权值,你可以随意给边加权,问你从a到b到c 的最短路是多少?
思路:
第一眼看感觉是三次最短路,我先是这么写的,先让每条边权为1,第一次最短路以a为起点得到(a->b)len1, 然后标记边,(a_>c)减去刚刚标记的边就是 len3,第二次最短路(b->c)得到的值减去遇到的标记的边 就是len2,然后给刚刚的边权排个序,比较下前(len1+len2)个边 和 前(len1+len1+len3)
上面很乱,看不懂没关系,是我比赛乱想的,后来随便想个图都能推翻那个结论,不过这题数据挺大的啊,我就没往正确想法(bfs 枚举点)上去想,还是太菜了…
正经的思路:
其实挺好懂得,先让边权为1,分别以 a,b,c 求最短路(边权为1,也就是bfs), 得到:disa,disb,disc ,然后 枚举n个点 :disa[i]+2*disb[i]+disc[i]
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pp;
const ll INF=1e18;
const int maxn=1e6+6;
const int mod=1e9+7;
const double eps=1e-9;
inline bool read(ll &num)
{char in;bool IsN=false;
in=getchar();if(in==EOF) return false;while(in!='-'&&(in<'0'||in>'9')) in=getchar();if(in=='-'){ IsN=true;num=0;}else num=in-'0';while(in=getchar(),in>='0'&&in<='9'){num*=10,num+=in-'0';}if(IsN) num=-num;return true;}
ll n,m,p;
ll num[maxn];
int head[maxn];
struct node{
int e,next;
ll w;
}edge[maxn];
ll cnt=0;
void addedge(int u,int v){
edge[cnt]=node{v,head[u],0ll};
head[u]=cnt++;
}
ll da[maxn],db[maxn],dc[maxn];
void bfs(int s,ll *dis){
queue<int>q;
for(int i=1;i<=n;i++) dis[i]=INF;
dis[s]=0;
q.push(s);
while(!q.empty()){
int u = q.front();q.pop();
for(int i=head[u];~i;i=edge[i].next){
int e=edge[i].e;
if(dis[e]>dis[u]+1){
dis[e]=dis[u]+1;
q.push(e);
}
}
}
}
ll s[maxn];
int main(){
int T;scanf("%d",&T);
while(T--){
ll a,b,c;
cnt=0;
read(n);read(m);read(a);read(b);read(c);
for(int i=0;i<=2*m+1;i++) head[i]=-1,edge[i].w=0;
for(int i=1;i<=m;i++) read(num[i]);
for(int i=1;i<=m;i++){
int x,y;scanf("%d%d",&x,&y);
addedge(x,y);
addedge(y,x);
}
s[0]=0;
sort(num+1,num+1+m);
for(int i=1;i<=m;i++) s[i]=s[i-1]+num[i];
bfs(a,da);
bfs(b,db);
bfs(c,dc);
ll ans=INF;
for(int i=1;i<=n;i++){
if(da[i]+db[i]+dc[i]>m) continue;
ans=min(ans,s[db[i]]+s[da[i]+db[i]+dc[i]]);
}
printf("%lld\n",ans);
}
return 0;
}