抗日战争时期,冀中平原的地道战曾发挥重要作用。
地道的多个站点间有通道连接,形成了庞大的网络。但也有隐患,当敌人发现了某个站点后,其它站点间可能因此会失去联系。
我们来定义一个危险系数DF(x,y):
对于两个站点x和y (x != y), 如果能找到一个站点z,当z被敌人破坏后,x和y不连通,那么我们称z为关于x,y的关键点。相应的,对于任意一对站点x和y,危险系数DF(x,y)就表示为这两点之间的关键点个数。
本题的任务是:已知网络结构,求两站点之间的危险系数。
输入数据第一行包含2个整数n(2 <= n <= 1000), m(0 <= m <= 2000),分别代表站点数,通道数;
接下来m行,每行两个整数 u,v (1 <= u, v <= n; u != v)代表一条通道;
最后1行,两个数u,v,代表询问两点之间的危险系数DF(u, v)。
1 3
2 3
3 4
3 5
4 5
5 6
1 6
关于并查集的相关概念,之前做 poj 2524已经总结,不清楚的可以看一下(POJ 2524 解题报告(并查集))
比如样例:
如果建立起并查集是:
遍历站点:
由于1即为查询的站点,所以从第2个站点开始遍历, 去掉站点2相关路径,再次建立并查集:
虽然去掉了站点2,但是站点1,站点6依然是连通的,故站点2不是关键点
去掉站点3及其相关路径:
这时,我们发现,站点1,站点6不连通,故站点3为关键点。同理去掉站点4,5,7,分别建立相应的并查集,可以发现,站点3,站点5是关键点,删掉其他站点,站点1与站点6之间的连通性不改变,所以样例答案是2
AC代码:
import java.util.Scanner;
public class Main {
static int team[];// 分组
static int road[][];// 路径
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
while (in.hasNext()) {
int count = 0;
int n = in.nextInt();
int m = in.nextInt();
road = new int[m][2];
team = new int[n + 1];
for (int i = 0; i < m; i++) {// 路径存储
road[i][0] = in.nextInt();
road[i][1] = in.nextInt();
}
int a = in.nextInt();
int b = in.nextInt();// 读入要查询连通性的两个站点
createSet(0);//没有0站点,故可以建立完整的关系,不会删掉任何一个站点
if (find(a) != find(b)) {
count = -1;//如果一开始就没有连通,那么直接输出-1
} else {
for (int i = 1; i <= n; i++) {
if (i != a && i != b) {
createSet(i);// 建立并查集
if (find(a) != find(b))// 判断是否连通
count++;
}
}
}
System.out.println(count);// 输出连通数
}
in.close();
}
private static void createSet(int key) {
for (int i = 1; i < team.length; i++) {// 初始化分组,刚开始每个人自己各位一组
team[i] = i;
}
for (int i = 0; i < road.length; i++) {
if (road[i][0] != key && road[i][1] != key) {
union(road[i][0], road[i][1]);// 合并
}
}
}
private static boolean union(int a, int b) {
int fa = find(a);
int fb = find(b);
if (fa != fb) {
team[fa] = fb;
return true;
}
return false;
}
private static int find(int k) {
if (k == team[k])
return k;
return team[k] = find(team[k]);
}
}