Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package com.williamfiset.algorithms.dp;

import java.util.*;

/**
* Dynamic Programming on Directed Acyclic Graphs (DAG).
*
* <p>
* This implementation demonstrates how to apply dynamic programming on a DAG
* using a topological ordering (Kahn's algorithm).
*
* <p>
* Example use-case: counting the number of ways to reach each node from a
* source.
*
* <p>
* Time Complexity: O(V + E)
* Space Complexity: O(V + E)
*/
public class DagDynamicProgramming {

// Minimal edge representation (only what is needed)
public static class Edge {
int to;

public Edge(int to) {
this.to = to;
}
}

/**
* Performs topological sorting using Kahn's algorithm.
*/
public static int[] kahnTopoSort(Map<Integer, List<Edge>> graph, int numNodes) {

int[] indegree = new int[numNodes];

// Compute indegree
for (int u = 0; u < numNodes; u++) {
for (Edge edge : graph.getOrDefault(u, Collections.emptyList())) {
indegree[edge.to]++;
}
}

Queue<Integer> q = new ArrayDeque<>();
for (int i = 0; i < numNodes; i++) {
if (indegree[i] == 0)
q.add(i);
}

int[] topo = new int[numNodes];
int index = 0;

while (!q.isEmpty()) {
int u = q.poll();
topo[index++] = u;

for (Edge edge : graph.getOrDefault(u, Collections.emptyList())) {
if (--indegree[edge.to] == 0) {
q.add(edge.to);
}
}
}

// Cycle detection
if (index != numNodes)
return new int[0];

return topo;
}

/**
* Counts number of ways to reach each node from a source in a DAG.
*/
public static long[] countWaysDAG(
Map<Integer, List<Edge>> graph, int source, int numNodes) {

int[] topo = kahnTopoSort(graph, numNodes);
if (topo.length == 0)
return null;

long[] dp = new long[numNodes];
dp[source] = 1;

for (int u : topo) {
if (dp[u] == 0L)
continue;

for (Edge edge : graph.getOrDefault(u, Collections.emptyList())) {
dp[edge.to] += dp[u];
}
}

return dp;
}

public static void main(String[] args) {

final int N = 6;
Map<Integer, List<Edge>> graph = new HashMap<>();

for (int i = 0; i < N; i++) {
graph.put(i, new ArrayList<>());
}

// Example DAG
graph.get(0).add(new Edge(1));
graph.get(0).add(new Edge(2));
graph.get(1).add(new Edge(3));
graph.get(2).add(new Edge(3));
graph.get(3).add(new Edge(4));

int source = 0;

long[] dp = countWaysDAG(graph, source, N);

if (dp == null) {
System.out.println("Graph contains a cycle!");
return;
}

System.out.println("Ways from source:");
System.out.println(Arrays.toString(dp));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package com.williamfiset.algorithms.dp;

import static org.junit.jupiter.api.Assertions.*;

import java.util.*;

import org.junit.jupiter.api.Test;

public class DagDynamicProgrammingTest {

private Map<Integer, List<DagDynamicProgramming.Edge>> createGraph(int n) {
Map<Integer, List<DagDynamicProgramming.Edge>> graph = new HashMap<>();
for (int i = 0; i < n; i++) {
graph.put(i, new ArrayList<>());
}
return graph;
}

@Test
public void testSimpleDAGWays() {

int n = 5;
Map<Integer, List<DagDynamicProgramming.Edge>> graph = createGraph(n);

graph.get(0).add(new DagDynamicProgramming.Edge(1));
graph.get(0).add(new DagDynamicProgramming.Edge(2));
graph.get(1).add(new DagDynamicProgramming.Edge(3));
graph.get(2).add(new DagDynamicProgramming.Edge(3));
graph.get(3).add(new DagDynamicProgramming.Edge(4));

long[] dp = DagDynamicProgramming.countWaysDAG(graph, 0, n);

assertNotNull(dp);
assertEquals(1, dp[0]);
assertEquals(1, dp[1]);
assertEquals(1, dp[2]);
assertEquals(2, dp[3]); // 0->1->3 and 0->2->3
assertEquals(2, dp[4]);
}

@Test
public void testDisconnectedGraph() {

int n = 4;
Map<Integer, List<DagDynamicProgramming.Edge>> graph = createGraph(n);

graph.get(0).add(new DagDynamicProgramming.Edge(1));

long[] dp = DagDynamicProgramming.countWaysDAG(graph, 0, n);

assertNotNull(dp);
assertEquals(1, dp[0]);
assertEquals(1, dp[1]);
assertEquals(0, dp[2]); // unreachable
assertEquals(0, dp[3]); // unreachable
}

@Test
public void testCycleDetection() {

int n = 3;
Map<Integer, List<DagDynamicProgramming.Edge>> graph = createGraph(n);

graph.get(0).add(new DagDynamicProgramming.Edge(1));
graph.get(1).add(new DagDynamicProgramming.Edge(2));
graph.get(2).add(new DagDynamicProgramming.Edge(0)); // cycle

long[] dp = DagDynamicProgramming.countWaysDAG(graph, 0, n);

assertNull(dp); // cycle detected
}
}
Loading