目录
一、前言
二、拓扑排序板子
三、题目:拓扑顺序
1、上链接
2、基本思路
3、代码
(1)C++(AC)
(2)python(AC)
四、题目:可达性统计
1、上链接
2、基本思路
3、代码
(1)C++(AC)
(2)python
一、前言
拓扑排序想必不用再多介绍了,下面来看看拓扑排序的一个板子(遍历某一个点的所有边),并做两道拓扑排序相关的例题。
二、拓扑排序板子
直接看代码。
#include<iostream>
#include<queue> // 拓扑排序经常需要用到队列
#include<cstring>
using namespace std;
const int N=10010;
int n,m; //顶点数,边数
int e[N],h[N],ne[N],idx; //某边终点、以某点为起点的第一条边、以某点为起点的下一条边、第idx条边
int degree[N]; //存储对应点的入度
void Add(int a,int b){
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void topsort(){
queue<int> q;
for(int i=1;i<=n;++i)
if(!degree[i])
q.push(i);
while(!q.empty()){
int t=q.front();
q.pop();
cout<<t<<" ";
for(int i=h[t];~i;i=ne[i]){
int j=e[i];
if(--degree[j]==0)
q.push(j);
}
}
cout<<endl;
}
int main(){
cin>>n>>m;
memset(h,-1,sizeof h); //所以~i能结束
for(int i=0;i<m;++i){
int a,b;
cin>>a>>b;
Add(a,b); //把当前的边存起来
degree[b]++;
}
topsort();
return 0;
}
三、题目:拓扑顺序
1、上链接
1639. 拓扑顺序 - AcWing题库
2、基本思路
对拓扑排序的板子进行相应的修改即可。
3、代码
(1)C++(AC)
#include<iostream>
#include<queue> // 拓扑排序经常需要用到队列
#include<cstring>
using namespace std;
const int N=10010;
const int K=110;
int n,m,k; //顶点数,边数
int e[N],h[N],ne[N],idx; //某边终点、以某点为起点的第一条边、以某点为起点的下一条边、第idx条边
int degree[N]; //存储对应点的入度
int ask[N],flg[K],d[N];
void Add(int a,int b){
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
int topsort(){
for(int i=1;i<=n;++i){
if(d[ask[i]])
return 0;
for(int j=h[ask[i]];~j;j=ne[j]){
d[e[j]]--;
}
}
return 1;
}
int main(){
cin>>n>>m;
memset(h,-1,sizeof h);
for(int i=0;i<m;++i){
int a,b;
cin>>a>>b;
Add(a,b); //把当前的边存起来
degree[b]++;
}
cin>>k;
for(int i=0;i<k;i++){
for(int j=1;j<=n;j++){
cin>>ask[j];
d[ask[j]]=degree[ask[j]];
//或这样写,都是一样的
// d[j] = degree[j]
}
if(topsort())
flg[i]=1;
else
flg[i]=0;
}
int i=0;
for(i=0;i<k;i++){
if(!flg[i]){
cout<<i;
break;
}
}
i++;
for(;i<k;++i){
if(!flg[i])
cout<<" "<<i;
}
cout<<endl;
return 0;
}
(2)python(AC)
N=10010
e=[0]*N
ne=[0]*N
h=[-1]*N
idx=0
degree=[0]*N
d=[0]*N
ask=[0]*N
flg=[0]*110
def Add(a,b):
global idx
e[idx]=b
ne[idx]=h[a]
h[a]=idx
idx=idx+1
def topsort():
for i in range(1,n+1):
if d[ask[i]]!=0:
return 0
j=h[ask[i]]
while ~j:
d[e[j]]-=1
j=ne[j]
return 1
n,m=map(int,input().split())
for i in range(m):
a,b=map(int,input().split())
Add(a,b)
degree[b]+=1
k=int(input())
for i in range(k):
lines=list(map(int,input().split()))
for j in range(1,n+1):
ask[j]=lines[j-1]
d[ask[j]]=degree[ask[j]]
if topsort():
flg[i]=1
else:
flg[i]=0
## print(flg[i],end="@")
## print("")
c=0
for i in range(k):
if c==0 and flg[i]==0:
print(i,end="")
c=1
elif flg[i]==0:
print(" {}".format(i),end="")
print("")
四、题目:可达性统计
1、上链接
164. 可达性统计 - AcWing题库
2、基本思路
对拓扑排序的板子进行相应的修改即可。
3、代码
(1)C++(AC)
#include<iostream>
#include<cstring>
#include<queue>
#include<bitset>
using namespace std;
const int N=30010;
int n,m;
int e[N],ne[N],h[N],idx;
int degree[N];
int seq[N];
bitset<N> f[N];
void Add(int a,int b){
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void topsort(){
queue<int> q;
for(int i=1;i<=n;++i)
if(degree[i]==0)
q.push(i);
int k=0;
while(!q.empty()){
int t=q.front();
q.pop();
seq[k++]=t;
for(int i=h[t];~i;i=ne[i]){
int j=e[i];
if(--degree[j]==0)
q.push(j);
}
}
}
int main(){
cin>>n>>m;
memset(h,-1,sizeof h);
for(int i=0;i<m;i++){
int a,b;
cin>>a>>b;
Add(a,b);
degree[b]++;
}
topsort();
for(int i=n-1;i>=0;i--){
int j=seq[i];
f[j][j]=1;
for(int p=h[j];~p;p=ne[p]){
f[j]|=f[e[p]];
}
}
for(int i=1;i<=n;++i){
cout<<f[i].count()<<endl;
}
return 0;
}
bitset用来处理二进制位非常方便。头文件是 #include<bitset>,bitset可能在PAT、蓝桥OJ中不常用,但是在LeetCode OJ中经常用到~而且知道 bitset 能简化一些操作,可能一些复杂的问题能够直接用 bitset 就很轻易的解决了。
bitset的用法随便百度一下就能找到一大堆,这里就不啰嗦了。
解释一下为什么“ f[j] |= f[e[p]] ”,如下图。(所以序列的遍历方向从后往前)
(2)python
我也想写一个python代码,在python中,bitset也确实可以写成一个类来实现,但是这个“类bitset”并不能起到优化空间的作用,该到题的内存会爆。而直接开一个二维列表来处理每个点的可达点数,内存无疑也会爆掉,故python代码我没能写出来。
稍微算一下题目的空间:
N和M的最大数值为 30000,即数组最大初始化有 30000 个int。
开 N^2 大小的空间,就有 9*10^8 个int,我们知道 10^6 个int约等于 4M,则 9*10^8 个int会有 3600M,题目空间限制为 256M。非常遗憾,内存爆了。
稍微解释一下 “10^6 个int约等于 4M”:
4M = 1024*4kb = 1024*1024*4byte = 10^6 个int
以上,拓扑排序
祝好