hdu 3416 Marriage Match IV(最短路+bfs+dfs)

匿名 (未验证) 提交于 2019-12-02 23:32:01

题意:

给定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
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!