题意:
给定n个点,m条单向边,每条边有花费,给出起点a和终点b,求a到b的最短路径的方案数,其中任意一条边至多只能出现在一个方案中。若两个点之间有多条边,算作不同的边。
题解:
做完之后百度了一下,发现清一色的最短路+最大流,想了想最后确实是最大流,然而只知道一点理论、不会网络流的本菜鸡确实不会这个做法,只能YY出别的解法。。。
首先,我们建两个图,一个正向的,一个反向的。
在正向的图上以点a为起点做最短路,得到点a到每个点的最短路。
在反向的图上以点b为起点做bfs,将满足dis[v]==dis[u]-w的边全部用队列存起来,得到一个由队列存起来的只包含了最短路径的边的图。
在队列存好的图上做多次dfs,每次都以点b为起点。
dfs时将路径出队列,若最后能到点a,则返回true,否则不断尝试直到无路可走,返回false。若dfs返回true,则方案数+1,重新dfs。
bfs和计算方案数时的时间复杂度为O(m),最短路采用的是堆优化的迪杰斯特拉,时间复杂度O(mlogm),因此总的时间复杂度为O(mlogm)
代码:
#include<bits/stdc++.h> using namespace std; #define ll long long #define db double #define m_p make_pair #define p_b push_back #define For(i,a,b) for(int i=a;i<=b;i++) #define ls (root<<1) #define rs ((root<<1)|1) #define mst(a,b) memset(a,b,sizeof(a)) const int MAXN=1e3+5; const db eps=1e-8; const int INF=0x3f3f3f3f; const int mod=1e9+7; const int seed=131; int dis[MAXN]; bool vis[MAXN]; struct Edge{ int v,w; Edge(int _v=0,int _w=0):v(_v),w(_w){} bool operator<(const Edge &p)const{ return w>p.w; } }; vector<Edge> G[MAXN],G1[MAXN]; priority_queue<Edge> pq; void dij(int s,int n){ mst(vis,0); mst(dis,INF); dis[s]=0; pq.push(Edge(s,0)); while(!pq.empty()){ int u=pq.top().v; pq.pop(); if(vis[u]) continue; vis[u]=1; for(Edge tt:G[u]){ int v=tt.v,w=tt.w; if(dis[v]>dis[u]+w){ dis[v]=dis[u]+w; pq.push(Edge(v,dis[v])); } } } } queue<int> q1[MAXN],q; bool dfs(int now,int s){ if(now==s) return true; int tmp; while(!q1[now].empty()){ tmp=q1[now].front(); q1[now].pop(); if(dfs(tmp,s)) return true; } return false; } int cal(int s,int e){ mst(vis,0); q.push(e); vis[e]=1; while(!q.empty()){ int u=q.front(); q.pop(); for(Edge tt:G1[u]){ int v=tt.v,w=tt.w; if(dis[v]==dis[u]-w){ q1[u].push(v); if(!vis[v]) q.push(v),vis[v]=1; } } } int res=0,cnt; while(!q1[e].empty()){ res+=dfs(e,s); } return res; } int main(){ // freopen("in.txt","r",stdin); int t,n,m; scanf("%d",&t); while(t--){ scanf("%d %d",&n,&m); For(i,1,n) G[i].clear(),G1[i].clear(); For(i,1,n){ while(!q1[i].empty()) q1[i].pop(); } int u,v,w; For(i,1,m){ scanf("%d %d %d",&u,&v,&w); G[u].p_b(Edge(v,w)); G1[v].p_b(Edge(u,w)); } int start,e; scanf("%d %d",&start,&e); dij(start,n); // cout<<dis[n]<<"\n"; int ans=cal(start,e); cout<<ans<<"\n"; } return 0; }
文章来源: https://blog.csdn.net/qq_38515845/article/details/89736932