给定具有N 个节点值从1 到 N和N – 1 条边的树。任务是在给定的树中找到最大匹配。
A matching in a tree is a collection of edges such that no pair of edges share a common node. Matching with the most edges is known as a maximum matching.
例子:
Input: Below is the given graph:
Output: 3
Explanation:
Set of Edges in the above graph for maximum matching:
(4, 5), (1, 2), (7, 8)
Input: Below is the given graph:
Output: 3
Explanation:
Set of Edges in the above graph for maximum matching:
(4, 5), (2, 3), (1, 7)
方法:这个问题可以使用 Greedy Approach 来解决,其思想是在树中使用后序遍历,从叶子边缘开始,向上移动。以下是步骤:
- 在具有根节点 1 的给定树上执行 DFS 遍历并使父节点为 0,并在递归 DFS 遍历中将当前节点作为节点的父节点传递。
- 在执行遍历时,对于每个节点U及其父节点P,如果这些节点未被访问,则将这些节点标记为已访问并将最大匹配计数增加 1。
- 在 DFS 遍历之后打印上述步骤中最大匹配的计数。
Greedy 算法是重复取任何叶子边缘。
TreeMatch(F:forest)
M <- []
while F nonempty do {
select any leaf-edge e
M <- M + [e]
F <- F - both ends of e
}
为什么贪心算法能正常工作?
让我们假设E是叶子边缘并考虑任何最大匹配N 。假设N不包含E 。然后,如果我们将E添加到N ,则现在只有一个顶点有两条边与之相关。所以我们可以删除N 的一条边并获得包含E的最大匹配。
下面是上述方法的实现:
C++
// C++ program for the above approach
#include
using namespace std;
#define N 10000
// Adjacency list to store edges
vector adj[N];
int used[N];
int max_matching;
// Add an edge between U and V in tree
void AddEdge(int u, int v)
{
// Edge from u to v
adj[u].push_back(v);
// Edge from V to U
adj[v].push_back(u);
}
// Function that finds the maximum
// matching of the DFS
void Matching_dfs(int u, int p)
{
for (int i = 0;
i < adj[u].size(); i++) {
// Go further as we are not
// allowed to go towards
// its parent
if (adj[u][i] != p) {
Matching_dfs(adj[u][i], u);
}
}
// If U and its parent P is
// not taken then we must
// take &mark them as taken
if (!used[u] and !used[p] and p != 0) {
// Increment size of edge set
max_matching++;
used[u] = used[p] = 1;
}
}
// Function to find the maximum
// matching in a graph
void maxMatching()
{
// Taking 1 as a root of the tree
Matching_dfs(1, 0);
// Print maximum Matching
cout << max_matching << "\n";
}
// Driver Code
int main()
{
int n = 5;
// Joining edge between
// two nodes in tree
AddEdge(1, 2);
AddEdge(1, 3);
AddEdge(3, 4);
AddEdge(3, 5);
// Function Call
maxMatching();
return 0;
}
Java
// Java program for the above approach
import java.util.*;
class GFG{
static final int N = 10000;
// Adjacency list to store edges
@SuppressWarnings("unchecked")
static Vector[] adj = new Vector[N];
static int used[] = new int[N];
static int max_matching;
// Add an edge between U and V in tree
static void AddEdge(int u, int v)
{
// Edge from u to v
adj[u].add(v);
// Edge from V to U
adj[v].add(u);
}
// Function that finds the maximum
// matching of the DFS
static void Matching_dfs(int u, int p)
{
for(int i = 0; i < adj[u].size(); i++)
{
// Go further as we are not
// allowed to go towards
// its parent
if (adj[u].get(i) != p)
{
Matching_dfs(adj[u].get(i), u);
}
}
// If U and its parent P is
// not taken then we must
// take &mark them as taken
if (used[u] == 0 &&
used[p] == 0 && p != 0)
{
// Increment size of edge set
max_matching++;
used[u] = used[p] = 1;
}
}
// Function to find the maximum
// matching in a graph
static void maxMatching()
{
// Taking 1 as a root of the tree
Matching_dfs(1, 0);
// Print maximum Matching
System.out.print(max_matching + "\n");
}
// Driver Code
public static void main(String[] args)
{
for(int i = 0; i < adj.length; i++)
adj[i] = new Vector();
// Joining edge between
// two nodes in tree
AddEdge(1, 2);
AddEdge(1, 3);
AddEdge(3, 4);
AddEdge(3, 5);
// Function call
maxMatching();
}
}
// This code is contributed by amal kumar choubey
Python3
# Python3 program for the above approach
N = 10000
# Adjacency list to store edges
adj = {}
used = [0 for i in range(N)]
max_matching = 0
# Add an edge between U and V in tree
def AddEdge(u, v):
if u not in adj:
adj[u] = []
if v not in adj:
adj[v] = []
# Edge from u to v
adj[u].append(v)
# Edge from V to U
adj[v].append(u)
# Function that finds the maximum
# matching of the DFS
def Matching_dfs(u, p):
global max_matching
for i in range(len(adj[u])):
# Go further as we are not
# allowed to go towards
# its parent
if (adj[u][i] != p):
Matching_dfs(adj[u][i], u)
# If U and its parent P is
# not taken then we must
# take &mark them as taken
if (not used[u] and not used[p] and p != 0):
# Increment size of edge set
max_matching += 1
used[u] = 1
used[p] = 1
# Function to find the maximum
# matching in a graph
def maxMatching():
# Taking 1 as a root of the tree
Matching_dfs(1, 0)
# Print maximum Matching
print(max_matching)
# Driver Code
n = 5
# Joining edge between
# two nodes in tree
AddEdge(1, 2)
AddEdge(1, 3)
AddEdge(3, 4)
AddEdge(3, 5)
# Function Call
maxMatching()
# This code is contributed by avanitrachhadiya2155
C#
// C# program for the above approach
using System;
using System.Collections.Generic;
class GFG{
static readonly int N = 10000;
// Adjacency list to store edges
static List[] adj = new List[N];
static int []used = new int[N];
static int max_matching;
// Add an edge between U and V in tree
static void AddEdge(int u, int v)
{
// Edge from u to v
adj[u].Add(v);
// Edge from V to U
adj[v].Add(u);
}
// Function that finds the maximum
// matching of the DFS
static void Matching_dfs(int u, int p)
{
for(int i = 0; i < adj[u].Count; i++)
{
// Go further as we are not
// allowed to go towards
// its parent
if (adj[u][i] != p)
{
Matching_dfs(adj[u][i], u);
}
}
// If U and its parent P is
// not taken then we must
// take &mark them as taken
if (used[u] == 0 &&
used[p] == 0 && p != 0)
{
// Increment size of edge set
max_matching++;
used[u] = used[p] = 1;
}
}
// Function to find the maximum
// matching in a graph
static void maxMatching()
{
// Taking 1 as a root of the tree
Matching_dfs(1, 0);
// Print maximum Matching
Console.Write(max_matching + "\n");
}
// Driver Code
public static void Main(String[] args)
{
for(int i = 0; i < adj.Length; i++)
adj[i] = new List();
// Joining edge between
// two nodes in tree
AddEdge(1, 2);
AddEdge(1, 3);
AddEdge(3, 4);
AddEdge(3, 5);
// Function call
maxMatching();
}
}
// This code is contributed by amal kumar choubey
Javascript
Python3
class Node:
def __init__(self, key):
self.left = None
self.right = None
self.val = key
def max_matching_helper(root):
if not root:
return (0, 0)
if not root.left and not root.right:
return (0, 0)
left_included, left_excluded = max_matching_helper(root.left)
right_included, right_excluded = max_matching_helper(root.right)
# Maximum matchin gincluding current node
curr_included = max(max(left_included, right_excluded) + 1, max(left_excluded, right_included) + 1)
# Maximum matching excluding current node
curr_excluded = left_included + right_included
return (curr_included, curr_excluded)
def max_matching(root):
# Taking 1 as a root of the tree
root_including, root_excluding = max_matching_helper(root)
# Return maximum Matching
return max(root_including, root_excluding)
# Driver code
root = Node(1)
root.left = Node(2)
root.right = Node(7)
root.left.left = Node(3)
root.left.right = Node(4)
root.left.right.left = Node(5)
root.left.right.right = Node(6)
root.right.left = Node(8)
root.right.right = Node(9)
print(max_matching(root))
# This code is contributed by Rathijeet Bhave
2
时间复杂度: O(V + E),其中 V 是边数,E 是边数。
辅助空间: O(V)
自下而上的 DFS 方法:
解决这个问题的另一种直观方法是自底向上使用DFS,每层返回两个值
包括当前节点的最大匹配
排除当前节点的最大匹配
我们将在左子树和右子树上递归并为它们获取这些值。然后我们可以根据这些值计算当前级别的新值。
设 left_included 表示包含左子树根的最大匹配,left_excluded 表示不包含左子树根的最大匹配。同样,对于 right_included 和 right_excluded。
如果我们将当前节点包含在最大匹配中,那么我们必须排除左子树根或右子树根之一。包含两者将导致当前节点重叠,这是不允许的。通过排除左子树根或右子树根,我们可以通过包含来自 current_node -> 左子树根或 current_node -> 右子树根的边之一,将最大匹配增加 1。
因此,包括当前节点的最大匹配将由下式给出
current_including = max(max(left_including, right_excluding) + 1, max(left_excluding, right_including) + 1)
如果我们排除当前节点,那么我们可以同时包含左子树根和右子树根。由于左右子树中的匹配是相互独立的,我们可以通过添加两个匹配来获得最大值。
因此,排除当前节点的最大匹配将由下式给出
current_excluding = left_including + right_including
我们将从当前递归级别返回这两个值到上递归级别。递归完成后,我们将收到两个值,包括根节点的最大匹配和不包括根节点的最大匹配。
这两者中的最大值将给出树中的最大匹配。
蟒蛇3
class Node:
def __init__(self, key):
self.left = None
self.right = None
self.val = key
def max_matching_helper(root):
if not root:
return (0, 0)
if not root.left and not root.right:
return (0, 0)
left_included, left_excluded = max_matching_helper(root.left)
right_included, right_excluded = max_matching_helper(root.right)
# Maximum matchin gincluding current node
curr_included = max(max(left_included, right_excluded) + 1, max(left_excluded, right_included) + 1)
# Maximum matching excluding current node
curr_excluded = left_included + right_included
return (curr_included, curr_excluded)
def max_matching(root):
# Taking 1 as a root of the tree
root_including, root_excluding = max_matching_helper(root)
# Return maximum Matching
return max(root_including, root_excluding)
# Driver code
root = Node(1)
root.left = Node(2)
root.right = Node(7)
root.left.left = Node(3)
root.left.right = Node(4)
root.left.right.left = Node(5)
root.left.right.right = Node(6)
root.right.left = Node(8)
root.right.right = Node(9)
print(max_matching(root))
# This code is contributed by Rathijeet Bhave
3
时间复杂度:O(V + E),其中 V 是边数,E 是边数。
辅助空间:O(V)
如果您希望与专家一起参加现场课程,请参阅DSA 现场工作专业课程和学生竞争性编程现场课程。