拓扑排序-toposort

最近终于把这个坑填了 ⋯ \cdots


什么是拓扑排序
对一个有向无环图 G G G进行拓扑排序,是将 G G G中所有顶点排成一个线性序列,使得图中
任意一对顶点 u u u v v v,若边 ( u , v ) ∈ E ( G ) (u,v)∈E(G) (u,v)E(G),则 u u u在线性序列中出现在 v v v之前。

拓扑排序的实现步骤

  1. 在有向图中选一个没有前驱的顶点并且输出
  2. 从图中删除所有和它有关的边)
  3. 重复上述两步,直至所有顶点输出,或者当前图中不存在无前驱的顶点为止,后者
    代表我们的有向图是有环的,因此,也可以通过拓扑排序来判断一个图是否有环。

注意事项

  1. 如果 A A A B B B之后,那么连边时是从 B → A B\to A BA连一条有向边(不要连反了啊)
  2. 判断有没有环:拓扑排序一遍之后,如果是 D A G DAG DAG所有点都恰好入队一次,如果有环,那么一定存在没有入队的点。
  3. 如何判断是不是拓扑:图论题 & & \&\& &&不是最短路 & & \&\& &&点与点之间有先后顺序
bool topsort(){                   //拓扑排序 
  int tot; 
  tot=0;                        //记录出队列的元素个数,判断是否有环  
  for(int i=1;i<=n;i++) 
  if(!r[i]) 
  { 
    q.push(i); 
    /*
    	根据题意
    */
  } 
  while(!q.empty())             //tot是顶点个数 
  { 
    int x=q.front(); 
    tot++; q.pop(); 
    for(int i=head[x];i;i=e[i].nex) 
    { 
      int yy=e[i].y; 
      r[yy]--;               //把所有从x到达的边的入度都减去1; 
      if(!r[yy])   //如果yy的入度为0,则说明从x到y的这条边是最后一条边
      { 
        q.push(yy);        //入度变为0的入队 
        /*
        	根据题意
        */
      }  
    }     
  } 
  if(tot==n) return true; 
  else  return false;  
}

例题
最大食物链计数
显然拓扑,如果一个点最后删到没有前驱也没有后继,那么它就是一条链的末尾
注意不要漏 % \% %,不然只有 40 p t s 40pts 40pts

#include <bits/stdc++.h>
#define int long long
using namespace std;
const long long Mod=80112002;
const int N=5000050;	
struct node{
	int nxt,to;
}e[N];
int fir[N],in[N],out[N];
queue<int> q;
int tot;
int n,m,a,b;
int f[N],ans;
inline void add(int x,int y){
	e[++tot].nxt=fir[x];
	e[tot].to=y;
	fir[x]=tot;
}
inline void topo(){
	for(int i=1;i<=n;++i){
		if(!in[i]) f[i]=1,q.push(i);
	}
	while(!q.empty()){
		int x=q.front();q.pop();
		for(int i=fir[x];i;i=e[i].nxt){
			int v=e[i].to;
			in[v]--;f[v]+=f[x];f[v]%=Mod;
			if(!in[v]){
				if(out[v]==0){
					ans+=f[v];
					ans=ans%Mod;
				}
				else q.push(v);
			}
		}
	}
	
}
inline int read(){
	int cnt=0,f=1;char c=getchar();
	while(!isdigit(c)){if(c=='-')f=-f;c=getchar();}
	while(isdigit(c)){cnt=(cnt<<1)+(cnt<<3)+(c^48);c=getchar();}
	return cnt*f;
}
signed main(){
	freopen("1.in","r",stdin);
	n=read(),m=read();
	for(int i=1;i<=m;++i){
		a=read(),b=read();
		add(b,a);in[a]++;out[b]++;
	}
	topo();
	cout<<ans%Mod<<endl;
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值