Skip to content

Commit fb3ba56

Browse files
add dp solution for maximum weighted matching in a tree
1 parent ce61a94 commit fb3ba56

File tree

3 files changed

+269
-0
lines changed

3 files changed

+269
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package com.thealgorithms.datastructures.graphs;
2+
3+
import java.util.ArrayList;
4+
import java.util.HashMap;
5+
import java.util.HashSet;
6+
7+
public class UndirectedAdjacencyListGraph {
8+
private ArrayList<HashMap<Integer, Integer>> adjacencyList = new ArrayList<>();
9+
10+
/**
11+
* Adds a new node to the graph by adding an empty HashMap for its neighbors.
12+
* @return the index of the newly added node in the adjacency list
13+
*/
14+
public int addNode() {
15+
adjacencyList.add(new HashMap<>());
16+
return adjacencyList.size() - 1;
17+
}
18+
19+
/**
20+
* Adds an undirected edge between the origin node (@orig) and the destination node (@dest) with the specified weight.
21+
* If the edge already exists, no changes are made.
22+
* @param orig the index of the origin node
23+
* @param dest the index of the destination node
24+
* @param weight the weight of the edge between @orig and @dest
25+
* @return true if the edge was successfully added, false if the edge already exists or if any node index is invalid
26+
*/
27+
public boolean addEdge(int orig, int dest, int weight) {
28+
int numNodes = adjacencyList.size();
29+
if (orig >= numNodes || dest >= numNodes || orig < 0 || dest < 0) {
30+
return false;
31+
}
32+
33+
if (adjacencyList.get(orig).containsKey(dest)) {
34+
return false;
35+
}
36+
37+
adjacencyList.get(orig).put(dest, weight);
38+
adjacencyList.get(dest).put(orig, weight);
39+
return true;
40+
}
41+
42+
/**
43+
* Returns the set of all adjacent nodes (neighbors) for the given node.
44+
* @param node the index of the node whose neighbors are to be retrieved
45+
* @return a HashSet containing the indices of all neighboring nodes
46+
*/
47+
public HashSet<Integer> getNeighbors(int node) {
48+
return new HashSet<>(adjacencyList.get(node).keySet());
49+
}
50+
51+
/**
52+
* Returns the weight of the edge between the origin node (@orig) and the destination node (@dest).
53+
* If no edge exists, returns null.
54+
* @param orig the index of the origin node
55+
* @param dest the index of the destination node
56+
* @return the weight of the edge between @orig and @dest, or null if no edge exists
57+
*/
58+
public Integer getEdgeWeight(int orig, int dest) {
59+
return adjacencyList.get(orig).getOrDefault(dest, null);
60+
}
61+
62+
/**
63+
* Returns the number of nodes currently in the graph.
64+
* @return the number of nodes in the graph
65+
*/
66+
public int size() {
67+
return adjacencyList.size();
68+
}
69+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package com.thealgorithms.dynamicprogramming;
2+
3+
import com.thealgorithms.datastructures.graphs.UndirectedAdjacencyListGraph;
4+
5+
/**
6+
* This class implements the algorithm for calculating the maximum weighted matching in a tree.
7+
* The tree is represented as an undirected graph with weighted edges.
8+
*
9+
* Problem Description:
10+
* Given an undirected tree G = (V, E) with edge weights γ: E → N and a root r ∈ V,
11+
* the goal is to find a maximum weight matching M ⊆ E such that no two edges in M
12+
* share a common vertex. The sum of the weights of the edges in M, ∑ e∈M γ(e), should be maximized.
13+
* For more Information: <a href="https://en.wikipedia.org/wiki/Matching_(graph_theory)">Matching (graph theory)</a>
14+
*
15+
* @author <a href="https://github.com/DenizAltunkapan">Deniz Altunkapan</a>
16+
*/
17+
public class TreeMatching {
18+
19+
private UndirectedAdjacencyListGraph graph;
20+
private int[][] dp;
21+
22+
/**
23+
* Constructor that initializes the graph and the DP table.
24+
*
25+
* @param graph The graph that represents the tree and is used for the matching algorithm.
26+
*/
27+
public TreeMatching(UndirectedAdjacencyListGraph graph) {
28+
this.graph = graph;
29+
this.dp = new int[graph.size()][2];
30+
}
31+
32+
/**
33+
* Calculates the maximum weighted matching for the tree, starting from the given root node.
34+
*
35+
* @param root The index of the root node of the tree.
36+
* @param parent The index of the parent node (used for recursion).
37+
* @return The maximum weighted matching for the tree, starting from the root node.
38+
*
39+
*/
40+
public int getMaxMatching(int root, int parent){
41+
if (root < 0 || root >= graph.size()) {
42+
throw new IllegalArgumentException("Invalid root: " + root);
43+
}
44+
MaxMatching(root, parent);
45+
return Math.max(dp[root][0],dp[root][1]);
46+
}
47+
48+
/**
49+
* Recursively computes the maximum weighted matching for a node, assuming that the node
50+
* can either be included or excluded from the matching.
51+
*
52+
* @param node The index of the current node for which the matching is calculated.
53+
* @param parent The index of the parent node (to avoid revisiting the parent node during recursion).
54+
*/
55+
private void MaxMatching(int node, int parent) {
56+
dp[node][0] = 0;
57+
dp[node][1] = 0;
58+
59+
int sumWithoutEdge = 0;
60+
for (int adjNode : graph.getNeighbors(node)) {
61+
if (adjNode == parent){
62+
continue;
63+
}
64+
MaxMatching(adjNode, node);
65+
sumWithoutEdge += Math.max(dp[adjNode][0], dp[adjNode][1]);
66+
}
67+
68+
dp[node][0] = sumWithoutEdge;
69+
70+
for (int adjNode : graph.getNeighbors(node)) {
71+
if (adjNode == parent){
72+
continue;
73+
}
74+
int weight = graph.getEdgeWeight(node, adjNode);
75+
dp[node][1] = Math.max(dp[node][1],
76+
sumWithoutEdge - Math.max(dp[adjNode][0], dp[adjNode][1]) + dp[adjNode][0] + weight);
77+
}
78+
}
79+
80+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
package com.thealgorithms.dynamicprogramming;
2+
3+
import org.junit.jupiter.api.BeforeEach;
4+
import org.junit.jupiter.api.Test;
5+
import com.thealgorithms.datastructures.graphs.UndirectedAdjacencyListGraph;
6+
import static org.junit.jupiter.api.Assertions.assertEquals;
7+
8+
class TreeMatchingTest {
9+
UndirectedAdjacencyListGraph graph;
10+
11+
@BeforeEach
12+
void setUp() {
13+
graph = new UndirectedAdjacencyListGraph();
14+
for (int i = 0; i < 14; i++) {
15+
graph.addNode();
16+
}
17+
}
18+
19+
@Test
20+
void testMaxMatchingForGeneralTree() {
21+
graph.addEdge(0, 1, 20);
22+
graph.addEdge(0, 2, 30);
23+
graph.addEdge(1, 3, 40);
24+
graph.addEdge(1, 4, 10);
25+
graph.addEdge(2, 5, 20);
26+
graph.addEdge(3, 6, 30);
27+
graph.addEdge(3, 7, 30);
28+
graph.addEdge(5, 8, 40);
29+
graph.addEdge(5, 9, 10);
30+
31+
TreeMatching treeMatching = new TreeMatching(graph);
32+
assertEquals(110,treeMatching.getMaxMatching(0,-1));
33+
}
34+
35+
@Test
36+
void testMaxMatchingForBalancedTree() {
37+
graph.addEdge(0, 1, 20);
38+
graph.addEdge(0, 2, 30);
39+
graph.addEdge(0, 3, 40);
40+
graph.addEdge(1, 4, 10);
41+
graph.addEdge(1, 5, 20);
42+
graph.addEdge(2, 6, 20);
43+
graph.addEdge(3, 7, 30);
44+
graph.addEdge(5, 8, 10);
45+
graph.addEdge(5, 9, 20);
46+
graph.addEdge(7, 10, 10);
47+
graph.addEdge(7, 11, 10);
48+
graph.addEdge(7, 12, 5);
49+
TreeMatching treeMatching = new TreeMatching(graph);
50+
assertEquals(100,treeMatching.getMaxMatching(0,-1));
51+
}
52+
53+
@Test
54+
void testMaxMatchingForTreeWithVariedEdgeWeights() {
55+
graph.addEdge(0, 1, 20);
56+
graph.addEdge(0, 2, 30);
57+
graph.addEdge(0, 3, 40);
58+
graph.addEdge(0, 4, 50);
59+
graph.addEdge(1, 5, 20);
60+
graph.addEdge(2, 6, 20);
61+
graph.addEdge(3, 7, 30);
62+
graph.addEdge(5, 8, 10);
63+
graph.addEdge(5, 9, 20);
64+
graph.addEdge(7, 10, 10);
65+
graph.addEdge(4, 11, 50);
66+
graph.addEdge(4, 12, 20);
67+
TreeMatching treeMatching = new TreeMatching(graph);
68+
assertEquals(140,treeMatching.getMaxMatching(0,-1));
69+
}
70+
71+
@Test
72+
void emptyTree() {
73+
TreeMatching treeMatching = new TreeMatching(graph);
74+
assertEquals(0,treeMatching.getMaxMatching(0,-1));
75+
}
76+
77+
@Test
78+
void testSingleNodeTree() {
79+
UndirectedAdjacencyListGraph singleNodeGraph = new UndirectedAdjacencyListGraph();
80+
singleNodeGraph.addNode();
81+
82+
TreeMatching treeMatching = new TreeMatching(singleNodeGraph);
83+
assertEquals(0, treeMatching.getMaxMatching(0, -1));
84+
}
85+
86+
@Test
87+
void testLinearTree() {
88+
graph.addEdge(0, 1, 10);
89+
graph.addEdge(1, 2, 20);
90+
graph.addEdge(2, 3, 30);
91+
graph.addEdge(3, 4, 40);
92+
93+
TreeMatching treeMatching = new TreeMatching(graph);
94+
assertEquals(60, treeMatching.getMaxMatching(0, -1));
95+
}
96+
97+
@Test
98+
void testStarShapedTree() {
99+
graph.addEdge(0, 1, 15);
100+
graph.addEdge(0, 2, 25);
101+
graph.addEdge(0, 3, 35);
102+
graph.addEdge(0, 4, 45);
103+
104+
TreeMatching treeMatching = new TreeMatching(graph);
105+
assertEquals(45, treeMatching.getMaxMatching(0, -1));
106+
}
107+
108+
@Test
109+
void testUnbalancedTree() {
110+
graph.addEdge(0, 1, 10);
111+
graph.addEdge(0, 2, 20);
112+
graph.addEdge(1, 3, 30);
113+
graph.addEdge(2, 4, 40);
114+
graph.addEdge(4, 5, 50);
115+
116+
TreeMatching treeMatching = new TreeMatching(graph);
117+
assertEquals(100, treeMatching.getMaxMatching(0, -1));
118+
}
119+
120+
}

0 commit comments

Comments
 (0)