可达性统计(拓扑排序,链式前向星)

拓扑排序

         演示过程(来自参考博客:https://blog.csdn.net/qq_41713256/article/details/80805338

 

 第一步:对所有入度为0的点入队列

 第二步:bfs搜索队列,并把该入度为0的点顺序存储到新数组中,再把所有与该点相连的点的入度减一

如上图所示,即实现对任意u->v,u都在v的左边

 

 

可达性统计

时间限制: 1 Sec  内存限制: 128 MB
提交: 110  解决: 33
[提交] [状态] [命题人:admin]

题目描述

给定一张N个点M条边的有向无环图,分别统计从每个点出发能够到达的点的数量。N,M≤30000。

 

输入

第一行两个整数N,M,接下来M行每行两个整数x,y,表示从x到y的一条有向边。

 

输出

共N行,表示每个点能够到达的点的数量。

 

样例输入

复制样例数据

10 10
3 8
2 3
2 5
5 9
5 9
2 3
3 9
4 8
2 10
4 9

样例输出

1
6
3
3
2
1
1
1
1
1

概念

1:入度:对一个点来说有指向该点的点,就说该点有入度,否则就没有

2:出度:与入度相反,对某一点来说有没有指向别的点

3:” | “,或运算,和bitset结合,一种新方法。。

 

关于如何通过“ | ”,实现统计数量,大体理解是:

 

对于n个点,经过拓扑排序后,实现了对任意u->v,u都在v的左边

再逆序统计每个点后面与该点相连的数量

 

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<iostream>
#include<math.h>
#include<queue>
#include<bitset>
typedef long long ll;
const int INF=1e9+7;
const int xmax=3e4+7;
using namespace std;

int head[xmax*2],cnt;
int inder[xmax];        ///每个点的入度
int tot;
int n,m,u,v;
int a[xmax];
bitset<xmax> c[xmax];

struct node{
    int to;
    int next;
}edge[xmax*2];
void add(int u,int v)
{
    edge[++cnt].to=v;
    edge[cnt].next=head[u];
    head[u]=cnt;
    inder[v]++;
}
void toposort()     ///拓扑排序
{
    queue<int> q;
    for(int i=1;i<=n;i++){
        if(inder[i]==0) q.push(i);
    }
    while(!q.empty()){
        int t=q.front();q.pop();    ///取出队列中首元素
        a[++tot]=t;
        ///对每一个入度为0的点进入a[]数组中,直到所有点都排完序,使得所有u->v的点,经过拓扑排序后都使u在v前面
        for(int i=head[t];i;i=edge[i].next){        ///链式前向星
            int tmp=edge[i].to;
            inder[tmp]--;          ///所有与t相连的点的入度减一
            if(inder[tmp]==0)   q.push(tmp);    ///当tmp这个点的入度为0,就加入队列q中
        }
    }
}
void solve()
{
    int t1,t2;
    for(int i=tot;i>=1;i--){    ///从后往前,因为最后一个点没有出度
        t1=a[i];
        c[t1][t1]=1;        ///(根据题意包含它本身,所以初始化为1)
        for(int j=head[t1];j;j=edge[j].next){
            t2=edge[j].to;
            c[t1]|=c[t2];       ///‘|’是 或 运算,对两个数进行二进制运算,对每一位 有一为一,如1|3=1|(11)=(11)=3,()表示二进制
           // cout<<c[t1][t1]<<endl;
        }
    }
}
int main()
{
    cin>>n>>m;
    while(m--){
        cin>>u>>v;
        add(u,v);
    }
    toposort();
    solve();
    for(int i=1;i<=n;i++)
        cout<<c[i].count()<<endl;
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值