给定从1到N标记的N个节点的无向图,任务是找到应从图中删除的最小标记的节点,以使生成的图不具有周期。
注意:如果初始图没有循环,即不需要删除任何节点,则打印-1。
例子:
Input: N = 5, edges[][] = {{5, 1}, {5, 2}, {1, 2}, {2, 3}, {2, 4}}
Output: 1
Explanation:
If node 1 is removed, the resultant graph has no cycle. Similarly, the cycle can be avoided by removing node 2 also.
Since we have to find the minimum labelled node, the answer is 1.
Input: N = 5, edges[][] = {{4, 5}, {4, 1}, {4, 2}, {4, 3}, {5, 1}, {5, 2}}
Output: 4
幼稚的方法:针对此问题的幼稚的方法是分别删除每个顶点,并检查结果图是否具有循环。这种方法的时间复杂度是二次的。
高效方法:想法是在给定图上应用深度优先搜索,并观察形成的dfs树。
- 后边缘称为不属于所构造DFS树的一部分的边缘,并且是某些节点v与v的祖先之一之间的边缘。
- 显然,图的所有那些不属于DFS树的边缘都是后边缘。
- 如果图中没有后沿,则图中没有循环。因此,在这种情况下,答案将为-1 。
如果图中有后边缘,那么我们需要找到最小边缘。为此,我们需要检查在从图形中删除特定边时是否删除了循环。因此,令v为我们当前正在检查的顶点。因此,顶点v必须遵循以下条件,以便在移除时不会导致循环:
- v必须位于连接图中每个后边缘端点的树路径上。
证明:假设存在一些后沿xy,使得v不在树路径上。如果删除v,我们仍然可以从x遍历到y,并通过后边缘返回x,这表明该循环未被删除。 - v的子树必须具有至少一个v祖先的后边缘。
证明:让子树S必须后退wx和yz的边缘,使得w和y在S中,而x和z是v的祖先。如果删除v,显然仍然存在一个循环,该循环由w到y之间的路径组成,从x到z的路径以及两个后边缘wx和yz(即循环)未删除。
因此,其思想是跟踪后边缘,并为节点的任何祖先节点的子树中的后边缘数量提供指示符。为了跟踪后边缘,我们将使用改良的DFS图形着色算法。
为了检查子树v是否具有至v的任何祖先的至少一个后边缘,我们实现dfs,使其返回v的子树的两个最高边缘的深度。我们维护一个数组,其中每个索引’如果节点’i’满足上述条件2,则存储在数组中的i’。类似地,实现了两个数组,一个用于子级,另一个用于父级,以查看节点v是否位于连接端点的树路径上。
下面是上述方法的实现:
C++
// C++ implementation to find the
// minimum labelled node to be
// removed such that there is no
// cycle in the undirected graph
#include
using namespace std;
const int MAX = 100005;
int totBackEdges;
int countAdj[MAX], small[MAX];
// Variables to store if a node V has
// at-most one back edge and store the
// depth of the node for the edge
int isPossible[MAX], depth[MAX];
vector adj[MAX];
int vis[MAX];
// Function to swap the pairs of the graph
void change(pair& p, int x)
{
// If the second value is
// greater than x
if (p.second > x)
p.second = x;
// Put the pair in the ascending
// order internally
if (p.first > p.second)
swap(p.first, p.second);
}
// Function to perform the DFS
pair dfs(int v, int p = -1, int de = 0)
{
// Initialise with the large value
pair answer(100000000, 100000000);
// Storing the depth of this vertex
depth[v] = de;
// Mark the vertex as visited
vis[v] = 1;
isPossible[v] = 1;
// Iterating through the graph
for (int u : adj[v]) {
// If the node is a child node
if (u ^ p) {
// If the child node is unvisited
if (!vis[u]) {
// Move to the child and increase
// the depth
auto x = dfs(u, v, de + 1);
// increase according to algorithm
small[v] += small[u];
change(answer, x.second);
change(answer, x.first);
// If the node is not having
// exactly K backedges
if (x.second < de)
isPossible[v] = 0;
}
// If the child is already visited
// and in current dfs
// (because colour is 1)
// then this is a back edge
else if (vis[u] == 1) {
totBackEdges++;
// Increase the countAdj values
countAdj[v]++;
countAdj[u]++;
small[p]++;
small[u]--;
change(answer, depth[u]);
}
}
}
// Colour this vertex 2 as
// we are exiting out of
// dfs for this node
vis[v] = 2;
return answer;
}
// Function to find the minimum labelled
// node to be removed such that
// there is no cycle in the undirected graph
int minNodetoRemove(
int n,
vector > edges)
{
// Construct the graph
for (int i = 0; i < edges.size(); i++) {
adj[edges[i].first]
.push_back(edges[i].second);
adj[edges[i].second]
.push_back(edges[i].first);
}
// Mark visited as false for each node
memset(vis, 0, sizeof(vis));
totBackEdges = 0;
// Apply dfs on all unmarked nodes
for (int v = 1; v <= n; v++) {
if (!vis[v])
dfs(v);
}
// If no backedges in the initial graph
// this means that there is no cycle
// So, return -1
if (totBackEdges == 0)
return -1;
int node = -1;
// Iterate through the vertices and
// return the first node that
// satisfies the condition
for (int v = 1; v <= n; v++) {
// Check whether the count sum of
// small[v] and count is the same as
// the total back edges and
// if the vertex v can be removed
if (countAdj[v] + small[v]
== totBackEdges
&& isPossible[v]) {
node = v;
}
if (node != -1)
break;
}
return node;
}
// Driver code
int main()
{
int N = 5;
vector > edges;
edges.push_back(make_pair(5, 1));
edges.push_back(make_pair(5, 2));
edges.push_back(make_pair(1, 2));
edges.push_back(make_pair(2, 3));
edges.push_back(make_pair(2, 4));
cout << minNodetoRemove(N, edges);
}
Java
// Java implementation to find the
// minimum labelled node to be
// removed such that there is no
// cycle in the undirected graph
import java.util.ArrayList;
import java.util.Arrays;
class Pair
{
int first, second;
public Pair(int first, int second)
{
this.first = first;
this.second = second;
}
}
class GFG{
static final int MAX = 100005;
static int totBackEdges;
static int[] countAdj = new int[MAX];
static int[] small = new int[MAX];
// Variables to store if a node V has
// at-most one back edge and store the
// depth of the node for the edge
static int[] isPossible = new int[MAX];
static int[] depth = new int[MAX];
@SuppressWarnings("unchecked")
static ArrayList[] adj = new ArrayList[MAX];
static int[] vis = new int[MAX];
// Function to swap the pairs of the graph
static void change(Pair p, int x)
{
// If the second value is
// greater than x
if (p.second > x)
p.second = x;
// Put the Pair in the ascending
// order internally
if (p.first > p.second)
{
int tmp = p.first;
p.first = p.second;
p.second = tmp;
}
}
// Function to perform the DFS
static Pair dfs(int v, int p, int de)
{
// Initialise with the large value
Pair answer = new Pair(100000000, 100000000);
// Storing the depth of this vertex
depth[v] = de;
// Mark the vertex as visited
vis[v] = 1;
isPossible[v] = 1;
// Iterating through the graph
for(int u : adj[v])
{
// If the node is a child node
if ((u ^ p) != 0)
{
// If the child node is unvisited
if (vis[u] == 0)
{
// Move to the child and increase
// the depth
Pair x = dfs(u, v, de + 1);
// increase according to algorithm
small[v] += small[u];
change(answer, x.second);
change(answer, x.first);
// If the node is not having
// exactly K backedges
if (x.second < de)
isPossible[v] = 0;
}
// If the child is already visited
// and in current dfs
// (because colour is 1)
// then this is a back edge
else if (vis[u] == 1)
{
totBackEdges++;
// Increase the countAdj values
countAdj[v]++;
countAdj[u]++;
small[p]++;
small[u]--;
change(answer, depth[u]);
}
}
}
// Colour this vertex 2 as
// we are exiting out of
// dfs for this node
vis[v] = 2;
return answer;
}
// Function to find the minimum labelled
// node to be removed such that
// there is no cycle in the undirected graph
static int minNodetoRemove(int n, ArrayList edges)
{
// Construct the graph
for(int i = 0; i < edges.size(); i++)
{
adj[edges.get(i).first].add(
edges.get(i).second);
adj[edges.get(i).second].add(
edges.get(i).first);
}
// Mark visited as false for each node
Arrays.fill(vis, 0);
totBackEdges = 0;
// Apply dfs on all unmarked nodes
for(int v = 1; v <= n; v++)
{
if (vis[v] == 0)
dfs(v, -1, 0);
}
// If no backedges in the initial graph
// this means that there is no cycle
// So, return -1
if (totBackEdges == 0)
return -1;
int node = -1;
// Iterate through the vertices and
// return the first node that
// satisfies the condition
for(int v = 1; v <= n; v++)
{
// Check whether the count sum of
// small[v] and count is the same as
// the total back edges and
// if the vertex v can be removed
if ((countAdj[v] + small[v] == totBackEdges) &&
isPossible[v] != 0)
{
node = v;
}
if (node != -1)
break;
}
return node;
}
// Driver code
public static void main(String[] args)
{
int N = 5;
ArrayList edges = new ArrayList<>();
for(int i = 0; i < MAX; i++)
{
adj[i] = new ArrayList<>();
}
edges.add(new Pair(5, 1));
edges.add(new Pair(5, 2));
edges.add(new Pair(1, 2));
edges.add(new Pair(2, 3));
edges.add(new Pair(2, 4));
System.out.println(minNodetoRemove(N, edges));
}
}
// This code is contributed by sanjeev2552
Python3
# Python3 implementation to find the
# minimum labelled node to be
# removed such that there is no
# cycle in the undirected graph
MAX = 100005;
totBackEdges = 0
countAdj = [0 for i in range(MAX)]
small = [0 for i in range(MAX)]
# Variables to store if a node V has
# at-most one back edge and store the
# depth of the node for the edge
isPossible = [0 for i in range(MAX)]
depth = [0 for i in range(MAX)]
adj = [[] for i in range(MAX)]
vis = [0 for i in range(MAX)]
# Function to swap the pairs of the graph
def change(p, x):
# If the second value is
# greater than x
if (p[1] > x):
p[1] = x;
# Put the pair in the ascending
# order internally
if (p[0] > p[1]):
tmp = p[0];
p[0] = p[1];
p[1] = tmp;
# Function to perform the DFS
def dfs(v, p = -1, de = 0):
global vis, totBackEdges
# Initialise with the large value
answer = [100000000, 100000000]
# Storing the depth of this vertex
depth[v] = de;
# Mark the vertex as visited
vis[v] = 1;
isPossible[v] = 1;
# Iterating through the graph
for u in adj[v]:
# If the node is a child node
if ((u ^ p) != 0):
# If the child node is unvisited
if (vis[u] == 0):
# Move to the child and increase
# the depth
x = dfs(u, v, de + 1);
# increase according to algorithm
small[v] += small[u];
change(answer, x[1]);
change(answer, x[0]);
# If the node is not having
# exactly K backedges
if (x[1] < de):
isPossible[v] = 0;
# If the child is already visited
# and in current dfs
# (because colour is 1)
# then this is a back edge
elif (vis[u] == 1):
totBackEdges += 1
# Increase the countAdj values
countAdj[v] += 1
countAdj[u] += 1
small[p] += 1
small[u] -= 1
change(answer, depth[u]);
# Colour this vertex 2 as
# we are exiting out of
# dfs for this node
vis[v] = 2;
return answer;
# Function to find the minimum labelled
# node to be removed such that
# there is no cycle in the undirected graph
def minNodetoRemove( n, edges):
# Construct the graph
for i in range(len(edges)):
adj[edges[i][0]].append(edges[i][1]);
adj[edges[i][1]].append(edges[i][0]);
global vis, totBackEdges
# Mark visited as false for each node
vis = [0 for i in range(len(vis))]
totBackEdges = 0;
# Apply dfs on all unmarked nodes
for v in range(1, n + 1):
if (vis[v] == 0):
dfs(v);
# If no backedges in the initial graph
# this means that there is no cycle
# So, return -1
if (totBackEdges == 0):
return -1;
node = -1;
# Iterate through the vertices and
# return the first node that
# satisfies the condition
for v in range(1, n + 1):
# Check whether the count sum of
# small[v] and count is the same as
# the total back edges and
# if the vertex v can be removed
if ((countAdj[v] + small[v] == totBackEdges) and isPossible[v] != 0):
node = v;
if (node != -1):
break;
return node;
# Driver code
if __name__=='__main__':
N = 5;
edges = []
edges.append([5, 1]);
edges.append([5, 2]);
edges.append([1, 2]);
edges.append([2, 3]);
edges.append([2, 4]);
print(minNodetoRemove(N, edges));
# This code is contributed by Pratham76
C#
// C# implementation to find the
// minimum labelled node to be
// removed such that there is no
// cycle in the undirected graph
using System;
using System.Collections;
using System.Collections.Generic;
class GFG
{
static int MAX = 100005;
static int totBackEdges;
static int []countAdj = new int[MAX];
static int []small = new int[MAX];
// Variables to store if a node V has
// at-most one back edge and store the
// depth of the node for the edge
static int []isPossible = new int[MAX];
static int []depth = new int[MAX];
static ArrayList adj = new ArrayList();
static int []vis = new int[MAX];
class pair
{
public int first, second;
public pair(int first, int second)
{
this.first = first;
this.second = second;
}
}
// Function to swap the pairs of the graph
static void change(ref pair p, int x)
{
// If the second value is
// greater than x
if (p.second > x)
p.second = x;
// Put the pair in the ascending
// order internally
if (p.first > p.second)
{
int tmp = p.first;
p.first = p.second;
p.second = tmp;
}
}
// Function to perform the DFS
static pair dfs(int v, int p = -1, int de = 0)
{
// Initialise with the large value
pair answer = new pair(100000000, 100000000);
// Storing the depth of this vertex
depth[v] = de;
// Mark the vertex as visited
vis[v] = 1;
isPossible[v] = 1;
// Iterating through the graph
foreach (int u in (ArrayList)adj[v]) {
// If the node is a child node
if ((u ^ p) != 0) {
// If the child node is unvisited
if (vis[u] == 0) {
// Move to the child and increase
// the depth
pair x = dfs(u, v, de + 1);
// increase according to algorithm
small[v] += small[u];
change(ref answer, x.second);
change(ref answer, x.first);
// If the node is not having
// exactly K backedges
if (x.second < de)
isPossible[v] = 0;
}
// If the child is already visited
// and in current dfs
// (because colour is 1)
// then this is a back edge
else if (vis[u] == 1) {
totBackEdges++;
// Increase the countAdj values
countAdj[v]++;
countAdj[u]++;
small[p]++;
small[u]--;
change(ref answer, depth[u]);
}
}
}
// Colour this vertex 2 as
// we are exiting out of
// dfs for this node
vis[v] = 2;
return answer;
}
// Function to find the minimum labelled
// node to be removed such that
// there is no cycle in the undirected graph
static int minNodetoRemove(
int n,
ArrayList edges)
{
// Construct the graph
for (int i = 0; i < edges.Count; i++) {
((ArrayList)adj[((pair)edges[i]).first])
.Add(((pair)edges[i]).second);
((ArrayList)adj[((pair)edges[i]).second])
.Add(((pair)edges[i]).first);
}
// Mark visited as false for each node
Array.Fill(vis, 0);
totBackEdges = 0;
// Apply dfs on all unmarked nodes
for (int v = 1; v <= n; v++) {
if (vis[v] == 0)
dfs(v);
}
// If no backedges in the initial graph
// this means that there is no cycle
// So, return -1
if (totBackEdges == 0)
return -1;
int node = -1;
// Iterate through the vertices and
// return the first node that
// satisfies the condition
for (int v = 1; v <= n; v++) {
// Check whether the count sum of
// small[v] and count is the same as
// the total back edges and
// if the vertex v can be removed
if ((countAdj[v] + small[v] == totBackEdges) && isPossible[v] != 0) {
node = v;
}
if (node != -1)
break;
}
return node;
}
// Driver code
static void Main()
{
int N = 5;
ArrayList edges = new ArrayList();
for(int i = 0; i < MAX; i++)
{
adj.Add(new ArrayList());
}
edges.Add(new pair(5, 1));
edges.Add(new pair(5, 2));
edges.Add(new pair(1, 2));
edges.Add(new pair(2, 3));
edges.Add(new pair(2, 4));
Console.Write(minNodetoRemove(N, edges));
}
}
// This code is contributed by rutvik_56
1
时间复杂度: O(N + M) ,其中N是节点数,M是边数。
如果您希望与行业专家一起参加现场课程,请参阅《 Geeks现场课程》和《 Geeks现场课程美国》。