Algorithm 什么是查找图形心的有效算法?
图形质心是一个等距离或小于或等于(N/2)距离的顶点,其中N是通过该顶点连接的连接组件的大小?![需要纠正?!] CodeForces有一个问题,它要求找出每个顶点是否是质心,但每次只移除并替换一条边 我需要帮助来完善这个伪代码/算法 Loop all Vertices: Loop all edges: Position each edge in every empty edge position between two unconnected nodes Count Size of each Connected Component (*1). If all sizes are less than or equal N/2, Then return true 循环所有顶点: 循环所有边: 在两个未连接的节点之间的每个空边位置中定位每条边 每个连接组件的计数大小(*1)。 如果所有尺寸小于或等于N/2, 然后返回true 问题是,该算法将至少在O(N*M^2)内运行。这是不能接受的 我查了一下答案,但我找不出其他人使用的算法的高级抽象。你能帮助我理解这些解决方案是如何工作的吗Algorithm 什么是查找图形心的有效算法?,algorithm,graph-algorithm,depth-first-search,centroid,connected-components,Algorithm,Graph Algorithm,Depth First Search,Centroid,Connected Components,图形质心是一个等距离或小于或等于(N/2)距离的顶点,其中N是通过该顶点连接的连接组件的大小?![需要纠正?!] CodeForces有一个问题,它要求找出每个顶点是否是质心,但每次只移除并替换一条边 我需要帮助来完善这个伪代码/算法 Loop all Vertices: Loop all edges: Position each edge in every empty edge position between two unconnected nodes Count Size of
(*1)我将尝试向您描述一种在线性时间内解决此问题的不太复杂的算法,以供将来参考,请参阅我的(它有一些注释) 其主要思想是,您可以在任意顶点上为树T求根并遍历它,对于每个顶点V,您可以执行以下操作:
- 从T上切下子树V
- 找到最重的顶点H,其大小我将尝试为您描述一种在线性时间内解决此问题的不太复杂的算法,供将来参考,请参阅我的(它有一些注释)
其主要思想是,您可以在任意顶点上为树T求根并遍历它,对于每个顶点V,您可以执行以下操作:
- 从T上切下子树V
- 找到具有大小的最重顶点H。那么,树的质心可以在O(N)空间和时间复杂度中确定。
- 构造一个表示树的矩阵,行索引表示N个节点,第i行中的元素表示第i个节点所连接的节点。您也可以使用任何其他表示
- 维护2个大小为N的线性阵列,索引i分别表示第i个节点(深度)的深度和第i个节点(父节点)的父节点(父节点)
- 还要维护另外两个线性数组,第一个包含树(队列)的BFS遍历序列,另一个(剩余)包含值[N-在该节点根的子树中的节点数]。换句话说,第i个索引包含当第i个节点及其所有子节点从树中移除时,整个树中剩余的节点数
- 现在,以任意节点为根执行BFS遍历,并填充数组“parent”和“depth”。这需要O(N)时间复杂度。另外,在数组“队列”中记录遍历序列
- 从叶节点开始,添加位于该节点的子树中存在的节点数,并在数组“leftOver”中的父索引处添加值。这也需要O(N)个时间,因为您可以使用已经准备好的“队列”数组并从后面移动
- 最后,遍历数组“剩余”并将每个值修改为[N-1-初始值]。“剩余”数组已准备就绪。成本:另一个O(N)
- 你的工作差不多完成了。现在,迭代这个“剩余”数组,并找到其值最接近地板(N/2)的索引。但是,该值无论如何不得超过地板(N/2) 此索引是树质心的索引。总体时间复杂度:O(N) Java代码:
import java.util.ArrayList; import java.util.Iterator; import java.util.Scanner; class Find_Centroid { static final int MAXN=100_005; static ArrayList<Integer>[] graph; static int[] depth,parent; // Step 2 static int N; static Scanner io=new Scanner(System.in); public static void main(String[] args) { int i; N=io.nextInt(); // Number of nodes in the Tree graph=new ArrayList[N]; for(i=0;i<graph.length;++i) graph[i]=new ArrayList<>(); //Initialisation for(i=1;i<N;++i) { int a=io.nextInt()-1,b=io.nextInt()-1; // Assuming 1-based indexing graph[a].add(b); graph[b].add(a); // Step 1 } int centroid = findCentroid(new java.util.Random().nextInt(N)); // Arbitrary indeed... ;) System.out.println("Centroid: "+(centroid+1)); // '+1' for output in 1-based index } static int[] queue=new int[MAXN],leftOver; // Step 3 static int findCentroid(int r) { leftOver=new int[N]; int i,target=N/2,ach=-1; bfs(r); // Step 4 for(i=N-1;i>=0;--i) if(queue[i]!=r) leftOver[parent[queue[i]]] += leftOver[queue[i]] +1; // Step 5 for(i=0;i<N;++i) leftOver[i] = N-1 -leftOver[i]; // Step 6 for(i=0;i<N;++i) if(leftOver[i]<=target && leftOver[i]>ach) // Closest to target(=N/2) but does not exceed it. { r=i; ach=leftOver[i]; } // Step 7 return r; } static void bfs(int root) // Iterative { parent=new int[N]; depth=new int[N]; int st=0,end=0; parent[root]=-1; depth[root]=1; // Parent of root is obviously undefined. Hence -1. // Assuming depth of root = 1 queue[end++]=root; while(st<end) { int node = queue[st++], h = depth[node]+1; Iterator<Integer> itr=graph[node].iterator(); while(itr.hasNext()) { int ch=itr.next(); if(depth[ch]>0) // 'ch' is parent of 'node' continue; depth[ch]=h; parent[ch]=node; queue[end++]=ch; // Recording the Traversal sequence } } } }
import java.util.ArrayList; 导入java.util.Iterator; 导入java.util.Scanner; 类查找_形心 { 静态最终整数最大值=100_005; 静态ArrayList[]图; 静态int[]深度,父级;//步骤2 静态int N; 静态扫描仪io=新扫描仪(System.in); 公共静态void main(字符串[]args) { int i; N=io.nextInt(); //树中的节点数 图形=新的阵列列表[N];
对于(i=0;i那么,树的质心可以在O(N)空间和时间复杂度中确定。- 构造一个表示树的矩阵,行索引表示N个节点,第i行中的元素表示第i个节点所连接的节点。您还可以使用任何其他表示
- 维护2个大小为N的线性阵列,索引i分别表示第i个节点(深度)的深度和第i个节点(父节点)的父节点(父节点)
- 还要维护另外两个线性数组,第一个包含树(队列)的BFS遍历序列,另一个(剩余)包含[N-在该节点根的子树中的节点数]。换句话说,第i个索引包含从树中删除第i个节点及其所有子节点时整个树中剩余的节点数
- 现在,以任意节点为根执行BFS遍历,并填充数组“parent”和“depth”。这需要O(N)个时间复杂度。另外,在数组“queue”中记录遍历序列
- 从叶节点开始,使用数组“leftOver”中父索引处的值,添加在该节点根目录子树中存在的节点数。这也需要O(N)个时间,因为您可以使用已准备好的“queue”数组并向后移动
- 最后,遍历数组“leftOver”,并将每个值修改为[N-1-初始值]。“leftOver”数组已准备就绪。成本:另一个O(N)
- 你的工作差不多完成了。现在,迭代这个“剩余”数组,找到其值最接近下限(N/2)的索引。但是,无论如何,这个值都不能超过下限(N/2)
此索引是树质心的索引。总体时间复杂度:O(N) Java代码:import java.util.ArrayList; import java.util.Iterator; import java.util.Scanner; class Find_Centroid { static final int MAXN=100_005; static ArrayList<Integer>[] graph; static int[] depth,parent; // Step 2 static int N; static Scanner io=new Scanner(System.in); public static void main(String[] args) { int i; N=io.nextInt(); // Number of nodes in the Tree graph=new ArrayList[N]; for(i=0;i<graph.length;++i) graph[i]=new ArrayList<>(); //Initialisation for(i=1;i<N;++i) { int a=io.nextInt()-1,b=io.nextInt()-1; // Assuming 1-based indexing graph[a].add(b); graph[b].add(a); // Step 1 } int centroid = findCentroid(new java.util.Random().nextInt(N)); // Arbitrary indeed... ;) System.out.println("Centroid: "+(centroid+1)); // '+1' for output in 1-based index } static int[] queue=new int[MAXN],leftOver; // Step 3 static int findCentroid(int r) { leftOver=new int[N]; int i,target=N/2,ach=-1; bfs(r); // Step 4 for(i=N-1;i>=0;--i) if(queue[i]!=r) leftOver[parent[queue[i]]] += leftOver[queue[i]] +1; // Step 5 for(i=0;i<N;++i) leftOver[i] = N-1 -leftOver[i]; // Step 6 for(i=0;i<N;++i) if(leftOver[i]<=target && leftOver[i]>ach) // Closest to target(=N/2) but does not exceed it. { r=i; ach=leftOver[i]; } // Step 7 return r; } static void bfs(int root) // Iterative { parent=new int[N]; depth=new int[N]; int st=0,end=0; parent[root]=-1; depth[root]=1; // Parent of root is obviously undefined. Hence -1. // Assuming depth of root = 1 queue[end++]=root; while(st<end) { int node = queue[st++], h = depth[node]+1; Iterator<Integer> itr=graph[node].iterator(); while(itr.hasNext()) { int ch=itr.next(); if(depth[ch]>0) // 'ch' is parent of 'node' continue; depth[ch]=h; parent[ch]=node; queue[end++]=ch; // Recording the Traversal sequence } } } }
导入ja