什么是拓扑序列:
在图论中,拓扑排序(Topological Sorting)是一个有向无环图(DAG, Directed Acyclic Graph)的所有顶点的线性序列。且该序列必须满足下面两个条件:
1:每个顶点出现且只出现一次。
2:若存在一条从顶点 A 到顶点 B 的路径,那么在序列中顶点 A 出现在顶点 B 的前面。
有向无环图(DAG)才有拓扑排序,非DAG图没有拓扑排序一说。
AOV网:
拓扑排序对应施工的流程图具有特别重要的作用,它可以决定哪些子工程必须要先执行,哪些子工程要在某些工程执行后才可以执行。为了形象地反映出整个工程中各个子工程(活动)之间的先后关系,可用一个有向图来表示,图中的顶点代表活动(子工程),图中的有向边代表活动的先后关系,即有向边的起点的活动是终点活动的前序活动,只有当起点活动完成之后,其终点活动才能进行。通常,我们把这种顶点表示活动、边表示活动间先后关系的有向图称做顶点活动网(Activity On Vertex network),简称AOV网。
一个AOV网应该是一个有向无环图,即不应该带有回路,因为若带有回路,则回路上的所有活动都无法进行(对于数据流来说就是死循环)。在AOV网中,若不存在回路,则所有活动可排列成一个线性序列,使得每个活动的所有前驱活动都排在该活动的前面,我们把此序列叫做拓扑序列(Topological order),由AOV网构造拓扑序列的过程叫做拓扑排序(Topological sort)。AOV网的拓扑序列不是唯一的,满足上述定义的任一线性序列都称作它的拓扑序列。
主要实现步骤:
1:建立有向边,记录终点边的入度
2:首先将入度为0的加入队列中
3:队列循环,删除入度为0的点及其依附的边,同时更新依附点入度,若为0加入队列中
#include<bits/stdc++.h>
#define ll long long
#define mem(a,b) memset(a,b,sizeof(a))
#define pii pair<int,int>
using namespace std;
const int maxn=3e4+5;
/**复杂度 O(V+E) 点数+边数**/
queue<int>q;
int indx[maxn];/**入度**/
vector<int>V;/**拓扑序列**/
vector<int>edge[maxn];/**邻接表**/
int main()
{
int n,m,u,v;
mem(indx,0);V.clear();
while(!q.empty()){q.pop();}
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
scanf("%d%d",&u,&v);
indx[v]++;
edge[u].push_back(v);
}
for(int i=1;i<=n;i++){
if(indx[i]==0){q.push(i);}
}
while(!q.empty()){
int pp=q.front();q.pop();
V.push_back(pp);
for(int i=0;i<edge[pp].size();i++){
int kk=edge[pp][i];
indx[kk]--;
if(indx==0){
q.push(kk);
}
}
}
if(V.size()==n){
cout<<"YES"<<endl;
}
else{
cout<<"存在环路"<<endl;
}
return 0;
}
相关例题:
思路:
1:对于’ >’ 和 ‘<’ 我们可以很好的处理,但 ‘=’ 该如何处理呢,为了减少相等之间的累赘建边,我们可以考虑将 ‘=’ 的集合分别用集合中一个根来代替,用并查集维护。
2:进行拓扑排序,若存在回路说明信息冲突;若拓扑序列存在,当且仅当每次队列中只有一个元素是才可以确定唯一的序列,即OK,反之是一个多种可能排列的拓扑序列即信息不全。
#include<bits/stdc++.h>
#define ll long long
#define pii pair<int,int>
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
const int maxn=1e4+5;
int indx[maxn],f[maxn];
queue<int>q;
vector<int>edge[maxn];
struct node
{
int x,y;
char ch;
}p[maxn<<1];
void init(int n)
{
for(int i=0;i<n;i++){
edge[i].clear();indx[i]=0;f[i]=i;
}
while(!q.empty()){q.pop();}
}
int getfind(int x)
{
return x==f[x]?x:f[x]=getfind(f[x]);
}
int main()
{
int n,m;
while(scanf("%d%d",&n,&m)!=EOF){
init(n);
for(int i=1;i<=m;i++){
scanf("%d %c %d",&p[i].x,&p[i].ch,&p[i].y);
if(p[i].ch=='='){/**将相等的集合归类**/
int x=getfind(p[i].x);
int y=getfind(p[i].y);
if(x!=y){f[y]=x;}
}
}
bool ans=false;
for(int i=1;i<=m;i++){
if(p[i].ch=='='){continue;}
int x=getfind(p[i].x),y=getfind(p[i].y);
if(x==y){/**前文出现过 '=' **/
ans=true;break;
}
if(p[i].ch=='>'){
edge[x].push_back(y);indx[y]++;
}
else{
edge[y].push_back(x);indx[x]++;
}
}
if(ans){cout<<"CONFLICT"<<endl;continue;}
int num=0;
for(int i=0;i<n;i++){
if(i==getfind(i)){
num++;/**实际存在的根数**/
if(!indx[i]){q.push(i);}
}
}
bool flag=true;
while(!q.empty()){
if(q.size()>1){flag=false;}/**每次队列中仅存1个元素时才能确定唯一序列**/
int pp=q.front();q.pop();
num--;
for(int i=0;i<edge[pp].size();i++){
int tt=edge[pp][i];
indx[tt]--;
if(indx[tt]==0){q.push(tt);}
}
}
if(num>0){cout<<"CONFLICT"<<endl;}
else if(!flag){cout<<"UNCERTAIN"<<endl;}
else if(flag){cout<<"OK"<<endl;}
}
return 0;
}
思路:
1:非常巧妙的应用了 STL bitset 容器
2:看到DAG应该较快想到拓扑排序;对当前点考虑,它能到达的点为 (1):直接与它相连; (2):与相连点相连的点;进行拓扑排序得到序列。
3:对序列分析:靠后的点若与前面点相连,那么它能到的点必然可以更新到前面点上。使用bitset进行状态记录,状态转移时直接异或即可。
#include<bits/stdc++.h>
#define ll long long
#define pii pair<int,int>
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
const int maxn=1e4+5;
int indx[maxn],ans[maxn];
queue<int>q;
vector<int>edge[maxn],v;
bitset<maxn> f[maxn];
int main()
{
mem(indx,0);
int n,m,x,y;
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
scanf("%d%d",&x,&y);
indx[y]++;
edge[x].push_back(y);
}
for(int i=1;i<=n;i++){
if(!indx[i]){q.push(i);}
}
while(!q.empty()){
int pp=q.front();q.pop();
v.push_back(pp);
for(int i=0;i<edge[pp].size();i++){
int tt=edge[pp][i];
if(--indx[tt]==0){q.push(tt);}
}
}
for(int i=v.size()-1;i>=0;i--){
int kk=v[i];
f[kk].reset();f[kk][kk]=1/** 1 代表可达点 **/
for(int j=0;j<edge[kk].size();j++){
f[kk]|=f[edge[kk][j]];/**状态转移**/
}
}
for(int i=1;i<=n;i++){
printf("%d\n",f[i].count());
}
return 0;
}
思路:反向建边,由低工资者指向高工资者。拓扑排序若存在环输出 “-1” ,反之将图转化为层的形式。详情见代码
#include<bits/stdc++.h>
#define ll long long
#define pii pair<int,int>
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
const int maxn=1e4+5;
int indx[maxn],ans[maxn];
queue<int>q;
vector<int>edge[maxn],v;
int main()
{
int n,m,x,y;
while(scanf("%d%d",&n,&m)!=EOF){
for(int i=1;i<=n;i++){
indx[i]=0;ans[i]=0;
edge[i].clear();
}
while(!q.empty()){q.pop();}
for(int i=1;i<=m;i++){
scanf("%d%d",&x,&y);
indx[x]++;
edge[y].push_back(x);
}
for(int i=1;i<=n;i++){
if(!indx[i]){q.push(i);}
}
int num=n;
while(!q.empty()){
int pp=q.front();q.pop();
num--;
for(int i=0;i<edge[pp].size();i++){
int tt=edge[pp][i];
if(--indx[tt]==0){q.push(tt);}
ans[tt]=max(ans[pp]+1,ans[tt]);/**更新该位置最低工资,不懂就模拟**/
}
}
if(num>0){cout<<"-1"<<endl;continue;}
int ant=0;
for(int i=1;i<=n;i++){
ant+=(888+ans[i]);
}
cout<<ant<<endl;
}
return 0;
}