E. Weights Distributing (最短路&贪心)

题目传送门

题意:给定无权无向图,m条边和m个权值和三个点a,b,c。问如何分配能使a到b b再到c 的权值和最小。

思路:先求出a,b,c到每个点的最短路(用BFS实现即可)考虑a–b与b—c的路径是否相交,若不相交,显然从权值较小到权值到大的取,若相交设交点为x 则路径为:
a–>x---->b—>x—>c ,由于b----x被加了两次,根据贪心则b–x路径取最小的权值,然后a—x----c依次取最小。

#include<bits/stdc++.h>
using namespace std;
const int inf=0x3f3f3f3f,N=2e5+5;
typedef long long ll;
ll w[N];
vector<int>e[N];
void bfs(int st,vector<int> &d){
	queue<int>q;
	d[st]=0;
	q.push(st);
	while(q.size()){
		int u=q.front();q.pop();
		for(auto v:e[u]){
			if(d[v]==inf) //由于相连结点距离为1所以可以通过d[]数组实现标记. 
				d[v]=d[u]+1,q.push(v);
		}
	}
}
int main(){
	int t;
	scanf("%d",&t);
	while(t--){
		int n,m,a,b,c;
		scanf("%d%d%d%d%d",&n,&m,&a,&b,&c);
		for(int i=1;i<=n;i++) e[i].clear();
		for(int i=1;i<=m;i++) scanf("%lld",&w[i]);
		sort(w+1,w+m+1);
		for(int i=1;i<=m;i++) w[i]+=w[i-1];//费用前缀和. 
		for(int i=1,u,v;i<=m;i++)
		{
			scanf("%d%d",&u,&v);
			e[u].push_back(v);
			e[v].push_back(u);
		}
		vector<int>da(n+1,inf);
		vector<int>db(n+1,inf);
		vector<int>dc(n+1,inf);
		bfs(a,da),bfs(b,db),bfs(c,dc);
		ll ans=1e18;
		for(int i=1;i<=n;i++)
		{
			 if(da[i]+db[i]+dc[i]>m) continue;//不能超过m条边的费用. 
			 ans=min(ans,w[db[i]]+w[db[i]+da[i]+dc[i]]);//贪心先对b-x路径用费用最小的. 
		}
		printf("%lld\n",ans);
		}
		return 0;
}