题意简述
给定 $ n $ 个点 $ m $ 条边的无向连通简单图,每条边为 $ a_i $ 到 $ b_i $,权值为 $ c_i $。你需要构造一棵生成树,最小化点 $ 1 $ 在生成树上到其它所有点的距离和,输出生成树的所有边的序号。如果有多个方案随便输出一个即可。
解题思路
我们令 d i s i dis_i disi 表示 1 1 1 到 i i i 的最短路的长度, D i D_i Di 表示在某种选择的方案下 1 1 1 到 i i i 的长度。不难发现一定会有 D i ≥ d i s i D_i \geq dis_i Di≥disi。所以,在选择某一条边后,如果有 D i = d i s i D_i=dis_i Di=disi,那么选择这条边就是最优的。选边的过程可以在跑 dijkstra 的过程中顺便完成掉。具体细节见代码及注释。
代码示例
#include<bits/stdc++.h>
using namespace std;
#define int long long
struct node{
int v,w,id;//id表示边的编号
bool operator<(const node &b)const{
return w>b.w;//方便priority_queue从小到大排序
}
};
vector<node> G[200010];
int n,m,ans[200010],dis[200010];
priority_queue<node> q;
void Dijkstra(){
memset(dis,0x3f,sizeof(dis));
q.push({1,0,0});
//其实q中不需要记录边的编号,这里偷了个懒,沿用了前面的结构体
dis[1]=0;
//初始化
while(!q.empty()){
int u=q.top().v,D=q.top().w;
q.pop();
if(dis[u]<D) continue;
//没有这个判断将会在after_contest的数据上TLE。我也不知道为什么会TLE,但是加上绝对是正确的。(如有大佬知道可以告诉我qwq)
for(int i=0;i<G[u].size();i++){
int v=G[u][i].v,w=G[u][i].w;
if(dis[v]>dis[u]+w){
dis[v]=dis[u]+w;
ans[v]=G[u][i].id;//记录答案
q.push({v,dis[v],0});
}
}
}
}
signed main(){
cin>>n>>m;
for(int i=1;i<=m;i++){
int u,v,w;
cin>>u>>v>>w;
G[u].push_back({v,w,i});
G[v].push_back({u,w,i});
}
Dijkstra();
for(int i=2;i<=n;i++) cout<<ans[i]<<" ";
cout<<endl;
return 0;
}