题目大意:
给定你一个由n个节点,m条边组成的图,再告诉你3个节点a,b,c和一个大小为m的数组p,
让你将p[i]赋值给每条边,使得a->b->c的路径长度最短。
链接:https://codeforces.com/contest/1343/problem/E
思路:
因为要使路径长度最短,所以边权要尽量小,所以我们先将p数组进行排序,然后预处理出他们的前缀和,
然后我们分别求出每个点到a的最短距离dis1[i],每个点到b的最短距离dis2[i],每个点到c的最短距离dis3[i],
我们发现a->b->c要么通过一个中间点,要么是一条直线,而第二种情况即中间点为b,因为dis2[i]存在重复,
所以我们将权值最小的p[i]赋给b->i之间的路径,剩下的路径再依次从小到大赋值,然后我们枚举每个点i,找到最小值即可。
代码:
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int MAXN=2e5+5; const ll INF=1e18+8; ll p[MAXN],sum[MAXN]; struct node { int to,nxt; }e[MAXN<<1]; int head[MAXN],tot,n,m,a,b,c; void add(int x,int y) { e[tot].to=y;e[tot].nxt=head[x];head[x]=tot++; } void add_edge(int x,int y) { add(x,y);add(y,x); } ll dis1[MAXN],dis2[MAXN],dis3[MAXN]; void bfs(int s,ll *dis) { queue<int>q; q.push(s);dis[s]=0; while(!q.empty()) { int u=q.front();q.pop(); for(int i=head[u];~i;i=e[i].nxt) { int v=e[i].to; if(!dis[v]&&v!=s) { dis[v]=dis[u]+1;q.push(v); } } } } int main() { ios::sync_with_stdio(false);cin.tie(0); int t;cin>>t; while(t--) { memset(head,-1,sizeof(head));tot=0; for(int i=0;i<=n;i++) dis1[i]=0,dis2[i]=0,dis3[i]=0; cin>>n>>m>>a>>b>>c; for(int i=1;i<=m;i++) { cin>>p[i]; } for(int i=1;i<=m;i++) { int x,y;cin>>x>>y;add_edge(x,y); } sort(p+1,p+1+m); for(int i=1;i<=m;i++) { sum[i]=sum[i-1]+p[i]; } bfs(a,dis1);bfs(b,dis2);bfs(c,dis3); ll ans=INF; for(int i=1;i<=n;i++) { if(dis1[i]+dis3[i]+dis2[i]<=m) { ans=min(ans,sum[dis1[i]+dis3[i]+dis2[i]]+sum[dis2[i]]); } } cout<<ans<<endl; } return 0; }