算法竞赛备赛——【图论】拓扑排序
拓扑排序算法
前置知识:
1.DAG图:一个无环的有向图,即有向无环图。
2.AOV网络:在⼀个表示⼯程的有向图中,⽤顶点表示活动,⽤弧表示活动之间的优先关系的有向图称为顶点表示活动的⽹(Activity On Vertex Network),简称AOV⽹。
拓扑排序:其实就是对⼀个DAG图构造拓扑序列的过程。
拓扑排序算法:kahn(卡恩)算法(基于BFS)和 基于DFS的算法。
kahn(卡恩)算法
可以判环
时间复杂度:O(V+E)
有环就没有拓扑序列
#include<bits/stdc++.h>
using namespace std;
using ll=long long;//有向图------>判环
int n,m;
struct Edge{//只求拓扑序列不用带边权 int to,next;
}e[10005];
int h[105];
int ind[105];//入度
int cnt;
int topo[105],k=0;void add(int u,int v){e[cnt].to=v;e[cnt].next=h[u];h[u]=cnt;cnt++;
} bool kahn(){queue<int> q;for(int i=1;i<=n;++i){if(ind[i]==0){q.push(i);}}int x,y;while(!q.empty()){x=q.front();q.pop();topo[++k]=x;//从1开始计数 for(int i=h[x];i!=-1;i=e[i].next) {y=e[i].to;ind[y]--;if(ind[y]==0) q.push(y); }}if(k==n) return true;//无环 else return false;//成环
} int main(){cin>>n>>m;memset(h,-1,sizeof h);int u,v;for(int i=1;i<=m;++i){cin>>u>>v;add(u,v);ind[v]++;}if(kahn()){for(int i=1;i<=k;++i){cout<<topo[i]<<" ";}} else{cout<<"有环\n"; }return 0;
}
例题
P1113 杂务 - 洛谷
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
int n;
struct Edge{int to,next;
}e[100005];
int h[10005],cnt;
int ind[10005];
int t[10005];//每个点需要的时间
int ed[10005];//每个点的完成时间 void add(int u,int v){e[cnt].to=v;e[cnt].next=h[u];h[u]=cnt;cnt++;
}void kahn(){queue<int> q;for(int i=1;i<=n;++i){if(ind[i]==0){q.push(i);ed[i]=t[i];//没有前置活动 }}int u,v;while(!q.empty()){u=q.front();q.pop();for(int i=h[u];~i;i=e[i].next){v=e[i].to;ind[v]--;ed[v]=max(ed[v],ed[u]+t[v]); //更新y的结束时间 if(ind[v]==0){q.push(v);}} }
}int main(){cin>>n;int u,v;memset(h,-1,sizeof h);for(int i=1;i<=n;++i){cin>>u;cin>>t[u];//注意不要同行读入while(cin>>v){if(v==0) break;add(u,v);ind[v]++;}}kahn();int ans=-1;for(int i=1;i<=n;++i){ans=max(ans,ed[i]);} cout<<ans<<endl;return 0;
}
[P1983 NOIP 2013 普及组] 车站分级 - 洛谷
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
int n,m;
vector<int> g[1005];
int s[1005];
int flag[1005];
int pd[1005][1005];//判断i--> j 的边是否已经建立
int ind[1005];
int le[1005];//级别 void kahn(){queue<int> q;for(int i=1;i<=n;++i){if(ind[i]==0){q.push(i);le[i]=1;}}int x,y;while(!q.empty()){x=q.front();q.pop();for(int i=0;i<g[x].size();i++){y=g[x][i];le[y]=max(le[y],le[x]+1);ind[y]--;if(ind[y]==0){q.push(y);}}}
}int main(){cin>>n>>m;int sn;for(int i=1;i<=m;++i){cin>>sn;//第i趟线路停靠sn个站 memset(flag,0,sizeof(flag));for(int j=1;j<=sn;++j){cin>>s[j];flag[s[j]]=1;//s[j]站停靠了 }for(int j=s[1];j<=s[sn];j++){//枚举该线路中所有没停靠的站 if(flag[j]==1) continue;for(int k=1;k<=sn;k++){if(pd[j][s[k]]==0){//避免重复建边 g[j].push_back(s[k]);pd[j][s[k]]=1;ind[s[k]]++;}} }}kahn();int ans=-1;for(int i=1;i<=n;++i){ans=max(ans,le[i]);}cout<<ans;return 0;
}
基于DFS的算法
时间复杂度:O(V+E)
直接求
#include<bits/stdc++.h>
using namespace std;
using ll=long long;//有向图------>保证是DAG图 无环
int n,m;
struct Edge{//只求拓扑序列不用带边权 int to,next;
}e[10005];
int h[105];
int ind[105];//入度
int cnt;
stack<int> topo;
int vis[105];void add(int u,int v){e[cnt].to=v;e[cnt].next=h[u];h[u]=cnt;cnt++;
} void DFS(int x){vis[x]=1;int y;for(int i=h[x];~i;i=e[i].next){y=e[i].to;if(vis[y]==0){DFS(y);}}topo.push(x);//返回时入栈
}int main(){cin>>n>>m;memset(h,-1,sizeof h);int u,v;for(int i=1;i<=m;++i){cin>>u>>v;add(u,v);ind[v]++;}for(int i=1;i<=n;++i){if(vis[i]==0){DFS(i);}}while(!topo.empty()){cout<<topo.top()<<" ";topo.pop();} return 0;
}
可以判环
#include<bits/stdc++.h>
using namespace std;
using ll=long long;//有向图------>判环
int n,m;
struct Edge{//只求拓扑序列不用带边权 int to,next;
}e[10005];
int h[105];
int ind[105];//入度
int cnt;
stack<int> topo;
int vis[105];
//vis[i]=0 还未走过/还未遍历过
// =1 该点及其后面所有的点已经访问完了---->回溯时标记为1
// =2 这个点走过一次了 现在正在遍历该点后面的点---->第一次访问标记为2
int flag=0;//标记是否有环 void add(int u,int v){e[cnt].to=v;e[cnt].next=h[u];h[u]=cnt;cnt++;
} void DFS(int x){vis[x]=2;int y;for(int i=h[x];~i;i=e[i].next){y=e[i].to;if(vis[y]==2){flag=1;//ans=y; //可以用ans来记录环从哪里开始 return ;} else if(vis[y]==0){DFS(y);if(flag==1){return ;}}}topo.push(x);//返回时入栈 vis[x]=1; //x及其后面的点访问结束
}int main(){cin>>n>>m;memset(h,-1,sizeof h);int u,v;for(int i=1;i<=m;++i){cin>>u>>v;add(u,v);ind[v]++;}for(int i=1;i<=n;++i){if(vis[i]==0){DFS(i);}}if(flag==1){cout<<"有环"<<endl;return 0; } while(!topo.empty()){cout<<topo.top()<<" ";topo.pop();} return 0;
}