广外oj 旅游计划(拓扑排序模板题)

Description
AC哥打算去很多个地方旅游,他已经列好了一份清单,一共有N个城市要去。但是AC哥还有个强迫症,就是对于某些城市,他要去安排在其它特定的一个甚至多个城市旅行完之后才去,于是他有制定了一份顺序计划。比如A城市如果要求排在B和C后面,那么他就必须先去B和C旅行,然后才能去A。
然而,聪明如AC哥,他竟然没注意到,他的计划表可能出现死循环,即诸如城市A安排在城市B后面,城市B安排在城市C后面,C又安排在A后面,这样最终其实他三个都去不了。现在,你需要根据这份计划表,告诉AC哥他有多少个城市实际上是去不了的。

Input
第一行输入一个整数T,表示有T组数据。
每组数据先输入一个整数N,M,表示有N个城市,以及M个限制条件。
接着一行有N个字符串,代表城市名字,字符串只包含小写字母,字符串之间用空格隔开。
接下来M行,每行都是两个字符串X和Y,表示去X之前一定要先去Y,这里X和Y保证不相同。
数据范围
1 <= T <= 20
1 <= N <= 10000
0 <= M <= 20000
城市名字都是长度不超过20,且仅由小写字母构成。

Output
对于每组数据,输出一行“Case #k: r”,其中k为数据编号,从1开始。r为结果,即AC哥最终没法去的城市的数量。

Sample Input
3
3 3
a b c
a b
b c
c a
3 2
guangzhou shenzhen dongguan
guangzhou shenzhen
guangzhou dongguan
4 2
guangzhou shenzhen dongguan huizhou
shenzhen huizhou
huizhou shenzhen

Sample Output
Case #1: 3
Case #2: 0
Case #3: 2

真心感谢大师兄。
大师兄天下第一!

拓扑排序的O(M+N)写法。

1、记录每个点的入度 2、把所有度数为0的点放到队列
3、循环处理队列,把队首元素取出来,然后把它指向的所有点的度数减一,如果有点的度数减到0了,就加到队列,直到队列空为止
复杂度O(N+M),N是点数,M是边数

public class Main {
    @SuppressWarnings("unchecked")
    static ArrayList<Integer> G[] = new ArrayList[10005];
    static int[] deg = new int[10005];
    static HashMap<String, Integer> name = new HashMap<String, Integer>();

    public static void main(String[] args) {
        // Scanner reader = new Scanner(System.in);
        InputReader reader = new InputReader();
        PrintWriter out = new PrintWriter(System.out);
        int t = reader.nextInt();
        int cases = 1;
        while (t-- > 0) {
            int n = reader.nextInt();
            int m = reader.nextInt();
            out.print("Case #" + (cases++) + ": ");
            for (int i = 1; i <= n; i++) {
                name.put(reader.next(), i);
            }

            for (int i = 1; i <= n; i++) {
                G[i] = new ArrayList<Integer>();
            }

            while (m-- > 0) {
                int to = name.get(reader.next());
                int from = name.get(reader.next());
                G[from].add(to);
                deg[to]++;
            }

            int count = 0;
            Queue<Integer> wait = new LinkedList<Integer>();

            for (int i = 1; i <= n; i++) {
                if (deg[i] > 0) {
                    continue;
                }
                wait.add(i);
                count++;
            }

            while (!wait.isEmpty()) {
                int get = wait.poll();
                for (int i = 0, len = G[get].size(); i < len; i++) {
                    if (--deg[G[get].get(i)] == 0) {
                        wait.add(G[get].get(i));
                        count++;
                    }
                }
            }

            out.println(n - count);
            name.clear();
            Arrays.fill(deg, 1, n + 1, 0);
        }
        out.close();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值