题目描述:
给定一张 N 个点 M 条边的有向无环图,分别统计从每个点出发能够到达的点的数量。
输入格式
第一行两个整数 N,M,接下来 M 行每行两个整数 x,y,表示从 x 到 y 的一条有向边。
输出格式
输出共 N 行,表示每个点能够到达的点的数量。
数据范围
1≤N,M≤30000
题目链接:可达性统计
思路:不能直接从某个结点向下去使用DFS查找它能到的点,因为对于a->b, a->c, b->d, c->d的情况,使用向下DFS得到 a 可达的点有4个,b , d , c , d,实际上只有3个,b , c, d。所有我们要另寻解法。
1.先根据各点入度进行拓扑排序,然后从后往前遍历各点能到的点,最后一个点只能到自身,若 i 点能到后面的某个点 j,则用 i 点并上后一个的点 j 能到的点的集合表示 i 能通过 j 到的所有点的集合。例如用 f(x)表示x能到的点的集合,假设 a 点能到的点是 b,b 点能到 c ,则a点能到的点的集合为:{a} U f (b) U f(c),因为是从后往前计算的,所以对于前一个点 i ,只需要并上后一个点 j 能到的集合即可,j 能到的所有点的集合肯定先算出来了的,即{a} U f (b)即可,f(b)已经是并上了f(c)的
2.用位图来存放每个点能否到其他点,对于第 i 位,1表示能到,0表示不能到,直接使用Java中的Bitset来处理,它有对应的进行位运算的方法。
下面的图表示位图的运算:
代码:
import java.util.ArrayList;
import java.util.BitSet;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;
public class Main {
static int[] de, sort; //各点入度、拓扑排序后的数组
static int n, m; //点数和边数
static ArrayList<ArrayList<Integer>> list = new ArrayList<>();//存放点的下一个点的集合
static BitSet[] bitSets; //BitSet是位图 list.get(i)就是i点的下一个点的集合
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt(); m = sc.nextInt();
de = new int[n+1];
sort = new int[n+1];
bitSets = new BitSet[n+1];//为位图数组申请空间
for (int i = 0; i <= n; i++) {
list.add(new ArrayList<>());//为集合分配空间
bitSets[i] = new BitSet(); //为各个位图申请空间
}
for (int i = 1; i <= m; i++) {
int a = sc.nextInt(), b = sc.nextInt();
list.get(a).add(b); //b点存入a点集合
de[b]++; //b点入度加一
}
topSort();
solve();
}
public static void topSort() {//拓扑排序
Queue<Integer> q = new LinkedList<>();
for (int i = 1; i <= n; i++)
if (de[i]==0)
q.offer(i);
int k = 1;
while(!q.isEmpty()) {
int a = q.poll();
sort[k++] = a;
ArrayList<Integer> tmp = list.get(a);
for (Integer next : tmp)
if (--de[next]==0)
q.offer(next);
}
}
public static void solve() {//位图的并集运算
for (int i = n; i >= 1; i--) {
int k = sort[i];
bitSets[k].set(k);//set方法是使第k位变为1
ArrayList<Integer> tmp = list.get(k);
for (Integer next : tmp) {//当前点并上下一个能到点的能到的集合
bitSets[k].or(bitSets[next]);//集合的并运算
}
}
for (int i = 1; i <= n ;i++) {
System.out.println(bitSets[i].cardinality());//输出位图中1的个数,表示能到的点数
}
}
}