Acwing 164.可达性统计(Java)

题目描述:

给定一张 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的个数,表示能到的点数
		}
	}
}

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Easenyang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值