最近觉得拓扑排序实在是一种优美的算法,不过我主要还是习惯bfs来拓扑排序,与很多人的dfs的习惯不符,但是bfs也很美,不是吗?
众所周知,只有有向无环图(DAG)才可以拓扑排序,其本质就是将图上的点按顺序排列,如果有环,那自然就不会有顺序了。想要进行拓扑排序,需要存储图的入度,如果这个点的入度为0,那么它的优先级最高,排在前面,当然,对于图而言,入度为0的点可以有多个,那么,谁在前面都无所谓。
今天讲基于bfs的拓扑排序,它有两种思路,即1.无前驱的顶点优先,2.无后继的顶点优先。这两种本质相同,只要讲第一种即可。
无前驱的顶点优先的BFS拓扑排序,其方法是1.先找到所以入度为0的点,放入队列,作为起点,这些点无所谓前后关系,如果没有入度为0的点,说明不是DAG,不存在拓扑序。2.弹出队首元素,其后继节点入度减1,将入度为0的后继放入队列。3.继续上述操作,直到队列清空。
很明显,总复杂度为O(V+E),并不高,可以解决较大数据规模的问题。
例题1:hdu 3342
Legal or NotTime Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 17150 Accepted Submission(s): 7960
We all know a master can have many prentices and a prentice may have a lot of masters too, it's legal. Nevertheless,some cows are not so honest, they hold illegal relationship. Take HH and 3xian for instant, HH is 3xian's master and, at the same time, 3xian is HH's master,which is quite illegal! To avoid this,please help us to judge whether their relationship is legal or not.
Please note that the "master and prentice" relation is transitive. It means that if A is B's master ans B is C's master, then A is C's master.
TO MAKE IT SIMPLE, we give every one a number (0, 1, 2,..., N-1). We use their numbers instead of their names.
If it is legal, output "YES", otherwise "NO".
#include <bits/stdc++.h> const int maxn=105; using namespace std; int ru[maxn]; using ll = long long; vector<int>g[maxn]; int main() { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int n,m; while(cin>>n>>m){ if(n==0&&m==0)break; memset(ru,0,sizeof(ru)); for(int i=0;i<n;i++)g[i].clear(); while(m--){ int x,y; cin>>x>>y; g[x].push_back(y); ru[y]++; } queue<int> q; for(int i=0;i<n;i++){ if(!ru[i])q.push(i); } while(!q.empty()){ int p=q.front();q.pop(); for(int i=0;i<g[p].size();i++){ int nex=g[p][i]; ru[nex]--; if(!ru[nex])q.push(nex); } } int flag=0; for(int i=0;i<n;i++){ if(ru[i]){ flag=1; break; } } if(flag)cout<<"NO"<<endl; else cout<<"YES"<<endl; } return 0; }
例题2:hdu 2647
RewardTime Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 19444 Accepted Submission(s): 6261
The workers will compare their rewards ,and some one may have demands of the distributing of rewards ,just like a's reward should more than b's.Dandelion's unclue wants to fulfill all the demands, of course ,he wants to use the least money.Every work's reward will be at least 888 , because it's a lucky number.
then m lines ,each line contains two integers a and b ,stands for a's reward should be more than b's.
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=10005; int ru[maxn]; int pri[maxn]; int main(){ int n,m; while(~scanf("%d%d",&n,&m)){ vector<int> g[maxn]; memset(ru,0,sizeof(ru)); fill(pri+1,pri+n+1,888); while(m--){ int x,y; scanf("%d%d",&x,&y); g[y].push_back(x); ru[x]++; } queue<int> q; for(int i=1;i<=n;i++){ if(!ru[i])q.push(i); } ll ans=0; while(!q.empty()){ int p=q.front();q.pop(); for(int i=0;i<(int)g[p].size();i++){ int nex=g[p][i]; ru[nex]--; pri[nex]=max(pri[nex],pri[p]+1); if(!ru[nex]){ q.push(nex); } } } for(int i=1;i<=n;i++)ans+=pri[i]; for(int i=1;i<=n;i++){ if(ru[i]){ ans=-1; break; } } printf("%lld\n",ans); } return 0; }
例题3:hdu 1285
确定比赛名次Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 59027 Accepted Submission(s): 21448
其他说明:符合条件的排名可能不是唯一的,此时要求输出时编号小的队伍在前;输入数据保证是正确的,即输入数据确保一定能有一个符合要求的排名。
#include<bits/stdc++.h> using namespace std; const int maxn=505; int main(){ int n,m; while(~scanf("%d%d",&n,&m)){ vector<int> g[maxn]; int ru[505]; int a[505]; while(m--){ int x,y; scanf("%d%d",&x,&y); g[x].push_back(y); ru[y]++; } priority_queue<int,vector<int>,greater<int> >q; for(int i=1;i<=n;i++){ if(!ru[i]){ q.push(i); } } int cnt=0; while(!q.empty()){ int p=(); q.pop(); a[++cnt]=p; for(int i=0;i<(int)g[p].size();i++){ ru[g[p][i]]--; if(!ru[g[p][i]])q.push(g[p][i]); } } for(int i=1;i<n;i++)printf("%d ",a[i]); printf("%d\n",a[n]); } return 0; }
摸了一天????,写一篇博客,假装做了一点事。
















