题目
N个人在举行一对一比拼的比赛。每个人的实力都是确定的,互不相同且不会改变。比赛的胜负总是由实力决定。因为是没有进行完所有比赛的状态,还无法准确看出谁是第一名,谁是第二名。
比如,假设知道下面一部分比赛的结果。参赛人员从1到N编号,箭头出发点上的人输给了箭头终点上的人。(胜←败)
[图1]
[图1]中可能成为第一的人有2,3,5,8这4名。因为第一名还没有确定,所以4个人都有可能成为第二名。如果有人只输给了2,3,5,8中的一个人,那么这个人也有可能是第二,但上图中没有这样的情况。4的情况虽然只输给了6号一个人,但6又输给了2和8,就不可能是第一名了,因此可以知道4只能是第三名以下。再看下面的情况。
[图2]
这个情况下可以确定8是第一名。因此8就不可能是第二了。只输给了8的1,2,6是有可能成为第二名的。
输入比赛进行的结果,编写程序,计算有可能是第二名的人的数量。
[限制条件]
- 人数N是2到100,000之间的整数。
- 比赛次数M所示0到200,000之间的整数。
- 一个人不能和自己进行比赛。
[输入]
第一行给出测试用例个数T。接着给出T个测试用例。每个测试用例的第一行给出人数N和比赛次数M。接下来的M行,每行给出代表一场比赛结果的2个人的编号A,B,用空格分隔。表示A赢了B。
[输出]
用标准输出按顺序输出各测试用例的答案。每个测试用例先输出"#x" (x是测试用例号码,从1开始,不要引号),然后输出一个空格后,输出有可能是第二名的人的数量。
[输入输出示例]
(输入)
3
8 7
8 1
8 6
2 1
2 6
6 4
2 7
3 7
8 10
8 1
8 6
8 2
8 4
2 5
2 7
2 3
3 7
4 3
6 4
4 3
1 2
3 4
1 2
(输出)
#1 4
#2 3
#3 4
思路
吐槽下,水题~~~
原图反向后,求拓扑排序可能在第二个位置的点个数即可(不用求拓扑排序结果,只考到了入度的概念而已)。
- 原图反向后求入度是0的点的个数P,若 P ==1,则该点为第一名,P重置为0;(若P > 1,则这些点都有可能是第二名,P保留);
- 求入度是0的点的可到达点中,入度是1的点个数Q(若入度大于1,则不可能是第二名);
- P + Q 即为所求。
注意点:Case中会给重复边,去重建议用Set。(List.Contains()方法为循环判断,效率太低,建议不要使用)
代码
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
import java.util.StringTokenizer;
public class Main {
static int T, N, M, ASW, IN[];
static Set<Integer>[] DATA;
public static void main(String[] args) throws Exception {
System.setIn(new FileInputStream("D:\\SW\\TestCase\\sample_input_20210924.txt"));
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
T = Integer.parseInt(br.readLine());
for (int t = 1; t <= T; t++) {
StringTokenizer st = new StringTokenizer(br.readLine());
N = Integer.parseInt(st.nextToken());
M = Integer.parseInt(st.nextToken());
ASW = N;
IN = new int[N + 1];
DATA = new HashSet[N + 1];
for (int n = 1; n <= N; n++)
DATA[n] = new HashSet<Integer>();
for (int a, b, m = 0; m < M; m++) {
st = new StringTokenizer(br.readLine());
a = Integer.parseInt(st.nextToken());
b = Integer.parseInt(st.nextToken());
if (DATA[a].add(b) && ++IN[b] == 1) ASW--;
}
if (ASW == 1) ASW = 0;
for (int n = 1; n <= N; n++) {
if (IN[n] > 0) continue;
for (int next : DATA[n])
if (IN[next] == 1) ASW++;
}
System.out.println(t + " " + ASW);
}
}
}