最短路径树SPT
对于最短路径树SPT,首先给你一张正权无向图,源点任意,然后要求你删除或者选择全部边的某些边,使得尽可能多的保持两点之间的最短路径,输出边的编号即可
首先从源点跑一遍最短路,在最短路中我们需要记录一个东西,还记得怎么输出路径嘛?是不是在遍历的时候记录一个$pre$数组即可?同样的,我们把这些前驱点保存下来,然后跑完最短路之后,对所有的点和边进行建图,那么我们就可以靠着判断是否为前驱节点来进行建图,由于除了源点之外的每个点只存在一个前驱节点(对于两点之间只有一个条边的,多条边的下面会讲),所以一共是 $n$ 个点, $n-1$ 条边, 这样我们就能构造出一个从源点为根的最短路径树
具体如下:
void dijkstra(int u){ dist[u]=0,que.push({0,u}); while(!que.empty()){ auto now=que.top(); que.pop(); int x=now.second,y=now.first; if(vis[x]) continue; vis[x]=true; for(int i=h[x];~i;i=ne[i]){ int j=e[i]; if(dist[j]>=y+w[i]){ dist[j]=y+w[i],pre[j]=x; que.push({dist[j],j}); } } } for(int i=1;i<=n;i++) for(int j=h[i];~j;j=ne[j]){ int x=e[j]; if(pre[x]==i) node[i].push_back(x); } }
那么回到原文,如何进行选择边而使得尽可能多的点保持最短距离呢?我们对最短路径树进行一个搜索,对于树上的任意点,其都是到源点的最短路径,那么我们可以直接做一遍 $dfs$ 或者 $bfs$ 保存即可
void dfs(int u,int fa){ for(auto x:node[u]){ if(res.size()>=k) return; if(x==fa) continue; res.push_back(id[{u,x}]),dfs(x,u); } }
例题:
本题就是为板子题,和上述思路一模一样
// LUOGU_RID: 153412284 #include <bits/stdc++.h> #define int long long using namespace std; const int N=3e5+10,mod=1e9+7; int e[2*N],ne[2*N],idx,h[2*N],w[2*N],pre[N],k,dist[N]; bool vis[N],st[N]; map<pair<int, int>,int>id; vector<int>node[N],res; void add(int a,int b,int c){ e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++; } void dijkstra(){ memset(dist,0x3f,sizeof dist); priority_queue<pair<int,int>,vector<pair<int,int>>,greater<pair<int,int>>>que; dist[1]=0,que.push({0,1}); while(!que.empty()){ auto now=que.top(); que.pop(); int x=now.second,y=now.first; if(vis[x]) continue; vis[x]=true; for(int i=h[x];~i;i=ne[i]){ int j=e[i]; if(dist[j]>y+w[i]){ dist[j]=y+w[i],pre[j]=x; que.push({dist[j],j}); } } } } void dfs(int u,int fa){ for(auto x:node[u]){ if(res.size()>=k) return; if(x==fa) continue; res.push_back(id[{u,x}]),dfs(x,u); } } signed main(){ std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0); int n,m; cin>>n>>m>>k; memset(h,-1,sizeof h); for(int i=1;i<=m;i++){ int a,b,c; cin>>a>>b>>c; id[{a,b}]=i,id[{b,a}]=i,add(a,b,c),add(b,a,c); } dijkstra(); for(int i=1;i<=n;i++) for(int j=h[i];~j;j=ne[j]) if(pre[e[j]]==i) node[i].push_back(e[j]); if(k>n-1) k=n-1; cout<<k<<endl,dfs(1,-1); for(auto x:res) cout<<x<<' '; }
本题与上题基本没有区别,要求的是边权和最小的最短路径树,那么我们在遍历的时候只需要求一下边权即可
#include <bits/stdc++.h> #define int long long using namespace std; const int N=1e6+10,mod=1e9+7; typedef pair<int,int>pii; int n,m,k,dist[N],pre[N],ans; int e[N],ne[N],w[N],idx,h[N]; map<pii,int>id; bool vis[N]; vector<int>node[N],res; priority_queue<pii,vector<pii>,greater<pii>>que; void add(int a,int b,int c){ e[idx]=b,ne[idx]=h[a],w[idx]=c,h[a]=idx++; } void dijkstra(int u){ dist[u]=0,que.push({0,u}); while(!que.empty()){ auto now=que.top(); que.pop(); int x=now.second,y=now.first; if(vis[x]) continue; vis[x]=true; for(int i=h[x];~i;i=ne[i]){ int j=e[i]; if(dist[j]>=y+w[i]){ dist[j]=y+w[i],pre[j]=x; que.push({dist[j],j}); } } } for(int i=1;i<=n;i++) for(int j=h[i];~j;j=ne[j]){ int x=e[j]; if(pre[x]==i) node[i].push_back(x); } } void dfs(int u,int fa){ for(auto x:node[u]){ if(u==fa) continue; ans+=(dist[x]-dist[u]),res.push_back(id[{u,x}]),dfs(x,u); } } signed main(){ std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0); cin>>n>>m; memset(h,-1,sizeof h); memset(dist,0x3f,sizeof dist); for(int i=1;i<=m;i++){ int a,b,c; cin>>a>>b>>c; add(a,b,c),add(b,a,c),id[{a,b}]=id[{b,a}]=i; } int st; cin>>st; dijkstra(st),dfs(st,-1); cout<<ans<<endl; for(auto x:res) cout<<x<<' '; }
CF1005F Berland and the Shortest Paths
该题总体思路差不多,不过问你的是有多少种保存路径的选择,所以我们可以记录所有的前驱节点,因为一个点可能有很多前驱节点,比如存在: $1---2---3$ 和 $1---4---3$ 那么 $2和4$ 的前驱节点都是 $3$ 所以 3 的贡献就是 2 ,那么我们直接进行乘法原理,求出可能性的总和,进行dfs即可
#include <bits/stdc++.h> #define endl '\n' #define int long long using namespace std; const int N=5e5+10,mod=1e9+7; typedef pair<int,int> pii; int n,m,k,dist[N],cnt,res=1; vector<int>g[N],node[N],pre[N]; map<pii,int>id; priority_queue<pii,vector<pii>,greater<pii>>que; bool vis[N],ok[N]; void dijkstra(){ dist[1]=0,que.push({0,1}); while(!que.empty()){ auto now=que.top(); que.pop(); int x=now.second,y=now.first; if(vis[x]) continue; vis[x]=true; for(auto u:g[x]){ if(dist[u]>y+1) dist[u]=y+1,que.push({dist[u],u}),pre[u].push_back(x); else if(dist[u]==y+1) pre[u].push_back(x); } } } void dfs(int u){ if(cnt>=res) return; if(u==n+1){ for(int i=1;i<=m;i++) cout<<ok[i]; return cout<<endl,cnt++,void(); } for(auto x:pre[u]) ok[id[{x,u}]]=true,dfs(u+1),ok[id[{x,u}]]=false; } signed main(){ std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0); cin>>n>>m>>k; memset(dist,0x3f,sizeof dist); for(int i=1;i<=m;i++){ int a,b; cin>>a>>b; g[a].push_back(b),g[b].push_back(a),id[{a,b}]=id[{b,a}]=i; } dijkstra(); for(int i=2;i<=n;i++){ if(res*pre[i].size()>k) {res=k;break;}; res*=pre[i].size(); } cout<<res<<endl,dfs(2); }