day54 图论章节刷题Part06(108.冗余连接、109.冗余连接II)
108.冗余连接
思路:从前向后遍历每一条边,边的两个节点如果不在同一个集合,就加入集合;如果边的两个节点已经出现在同一个集合里,说明边的两个节点已经连在一起了,再加入这条边一定会出现环。
代码如下:
import java.util.*;
public class Main{
public static void main (String[] args) {
Scanner scan=new Scanner(System.in);
int n=scan.nextInt();
DisJoint disJoint=new DisJoint(n+2);
for(int i=0;i<n;i++){
int u=scan.nextInt();
int v=scan.nextInt();
if(!disJoint.isSame(u,v)) disJoint.join(u,v);
else System.out.println(u+" "+v);
}
}
}
class DisJoint{
private int[] father;
public DisJoint(int N){
father=new int[N];
for(int i=0;i<N;i++){
father[i]=i;
}
}
public int find(int n){
return father[n]==n?n: (father[n]=find(father[n]));
}
public boolean isSame(int u,int v){
u=find(u);
v=find(v);
return u==v;
}
public void join(int u,int v){
u=find(u);
v=find(v);
if(u==v) return;
father[v]=u;
}
}
109.冗余连接II
有向图比无向图相对复杂,考虑多种情况:
- 情况一:如果找到入度为2的点,需要判断删除哪一条边,删除后本图能成为有向树。如果是删哪个都可以,优先删顺序靠后的边。
- 情况二: 如果没有入度为2的点,说明图中有环,删掉构成环的边。
代码如下:
import java.util.*;
public class Main{
public static void main (String[] args) {
Scanner scan=new Scanner(System.in);
int n=scan.nextInt();
List<int[]> edge=new ArrayList<>();
int[] inDegree=new int[n+1];
DisJoint disJoint=new DisJoint(n+1);
List<int[]> possibleEdges = new ArrayList<>();
for (int i = 0; i < n; i++) {
int s = scan.nextInt();
int t = scan.nextInt();
edge.add(new int[]{s, t});
inDegree[t]++;
if (!disJoint.isSame(s,t)) {
disJoint.join(s,t);
}
else possibleEdges.add(new int[]{s, t});
}
// 寻找入度为2的节点
int conflictNode = -1;
for (int i = 1; i <= n; i++) {
if (inDegree[i] == 2) {
conflictNode = i;
break;
}
}
// 如果存在入度为2的节点,逆向查找多余的边
if (conflictNode != -1) {
for (int i = n - 1; i >= 0; i--) {
if (edge.get(i)[1] == conflictNode) {
// 尝试删除这条边,检查是否还能构成有向树
if (checkTree(edge, i)) {
System.out.println(edge.get(i)[0] + " " + edge.get(i)[1]);
return;
}
}
}
}
// 如果存在可能的边,选择最后出现的那条边
if (!possibleEdges.isEmpty()) {
int[] lastEdge = possibleEdges.get(possibleEdges.size() - 1);
System.out.println(lastEdge[0] + " " + lastEdge[1]);
}
}
// 检查删除某条边后是否还能构成有向树
private static boolean checkTree(List<int[]> edge, int removeIndex) {
int n = edge.size();
DisJoint disJoint = new DisJoint(n + 1);
Set<Integer> nodes = new HashSet<>();
for (int i = 0; i < n; i++) {
if (i == removeIndex) continue;
int s = edge.get(i)[0];
int t = edge.get(i)[1];
nodes.add(s);
nodes.add(t);
if (disJoint.isSame(s, t)) {
return false; // 形成环
} else {
disJoint.join(s, t);
}
}
// 检查是否所有节点都在同一个连通分量中
for (int i = 1; i <= n; i++) {
if (nodes.contains(i) && !disJoint.isSame(1, i)) {
return false; // 不是连通的
}
}
return true;
}
}
class DisJoint{
private int[] father;
public DisJoint(int N){
father=new int[N];
for(int i=0;i<N;i++){
father[i]=i;
}
}
public int find(int n){
return father[n]==n?n: (father[n]=find(father[n]));
}
public boolean isSame(int u,int v){
u=find(u);
v=find(v);
return u==v;
}
public void join(int u,int v){
u=find(u);
v=find(v);
if(u==v) return;
father[v]=u;
}
}
原文地址:https://blog.csdn.net/m0_51007517/article/details/143607529
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!