原题链接:E. Weights Distributing
原题截图:
题目大意:
给出一张有n个节点和m条边的无向连通图,再给定m的权值,要求将这m个权值分配到m条边上,使得从a到b再从b到c的距离最短,输出最短距离
解题思路:
这个问题的难点有两个:
- 路径要求从a到b再从b到c
- 要自己分配边权值
那么首先简化问题:
如果是从x到y,得出一个分配的方案使得路径距离最小
那么自然想到的是,使得从x到y所经过的边最少,然后分配最小的权值即可
如果将所有边的权值初始化为1,求最短路就可以求出这个最短距离了。
再对于两条路 a->b 和 b->c ,可以想到最终的路径图无妨两种情况:
当然,第一种情况也可以看成是当M点与B点重合的第二种情况,那么就只用讨论第二种图的情形了
处理方法:
- 求出A到M的最短距离,M到B的最短距离(【无向图】也就是B到M的最短距离),M到C的最短距离(【无向图】也就是C到M的最短距离)
- 将最小的权值先分配给B到M的边(因为这些边经过了两次),再将其他的权值分配给A到M和C到M
那么,确定下这个M就可以求出最短距离了,M的确定自然遍历一下所有的节点就可以了
解题步骤:
- 将所有的边权值初始化为1,以A为起点求最短路得到数组disa,以B的起点求最短路得到数组disb,以C为起点求最短路得到数组disc【这里的disa[i]即为从点a到点i所经过的边的个数】
- 对p从小到大排序后,遍历所有节点m,根据上面讲的分配权值的原则,可得最短距离
ans=min(ans, ∑ i = 1 d i s b [ m ] p [ i ] \sum_{i=1}^{disb[m]} p[i] ∑i=1disb[m]p[i]+ ∑ i = 1 d i s a [ m ] + d i s b [ m ] + d i s c [ m ] p [ i ] \sum_{i=1}^{disa[m]+disb[m]+disc[m]} p[i] ∑i=1disa[m]+disb[m]+disc[m]p[i])
AC代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<climits>
using namespace std;
const int len=2e5+10;
int t,n,m,a,b,c,u,v;
long long p[len],ans;
int head[len],nxt[2*len],to[2*len],cnt;
int disa[len],disb[len],disc[len];
void init(){
for(int i=1;i<=n;i++){
head[i]=0;
disa[i]=INT_MAX;
disb[i]=INT_MAX;
disc[i]=INT_MAX;
}
for(int i=1;i<=2*m;i++){
nxt[i]=0;
}
cnt=0;
disa[a]=0;
disb[b]=0;
disc[c]=0;
}
void addEdge(int u,int v){
to[++cnt]=v;
nxt[cnt]=head[u];
head[u]=cnt;
}
struct s_node{
int id;
int dis;
s_node(int a,int b){
id=a;
dis=b;
}
bool operator < (const s_node &a) const {
return dis>a.dis;
}
};
int done[len];
void dijkstra(int s,int dis[]){
memset(done,0,sizeof(done));
priority_queue<s_node>node;
node.push(s_node(s,0));
while(!node.empty()){
s_node x=node.top();
node.pop();
if(done[x.id]){
continue;
}
done[x.id]=1;
for(int i=head[x.id];i;i=nxt[i]){
if(done[to[i]]){
continue;
}
if(dis[to[i]]>dis[x.id]+1){
dis[to[i]]=dis[x.id]+1;
node.push(s_node(to[i],dis[to[i]]));
}
}
}
}
int main(){
scanf("%d",&t);
while(t--){
scanf("%d%d%d%d%d",&n,&m,&a,&b,&c);
init();
for(int i=1;i<=m;i++){
scanf("%lld",&p[i]);
}
sort(p+1,p+1+m);
for(int i=1;i<=m;i++){
p[i]+=p[i-1];
}
for(int i=1;i<=m;i++){
scanf("%d%d",&u,&v);
addEdge(u,v);
addEdge(v,u);
}
dijkstra(a,disa);
dijkstra(b,disb);
dijkstra(c,disc);
ans=LLONG_MAX;
for(int i=1;i<=n;i++){
if(disa[i]+disb[i]+disc[i]>m){
continue;
}
ans=min(ans,p[disb[i]]+p[disa[i]+disb[i]+disc[i]]);
}
printf("%lld\n",ans);
}
return 0;
}