0% found this document useful (0 votes)
4 views19 pages

DP_Problems_Java

The document provides a comprehensive overview of dynamic programming techniques in Java, covering both 1D and 2D problems. It includes examples of recursion, memoization, and tabulation methods for various problems like climbing stairs, frog jumps, house robber, unique paths, and stock trading. Each problem is illustrated with code snippets demonstrating the different approaches to solving them.
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
4 views19 pages

DP_Problems_Java

The document provides a comprehensive overview of dynamic programming techniques in Java, covering both 1D and 2D problems. It includes examples of recursion, memoization, and tabulation methods for various problems like climbing stairs, frog jumps, house robber, unique paths, and stock trading. Each problem is illustrated with code snippets demonstrating the different approaches to solving them.
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

Dynamic Programming — Java

Recursion · Memoization · Tabulation | TUF+ DSA Checklist

■ 1D Dynamic Programming

1. Climbing Stairs
Recursion
int climbStairs(int n) {
if (n <= 1) return 1;
return climbStairs(n - 1) + climbStairs(n - 2);
}

Memoization
int climbStairs(int n, int[] memo) {
if (n <= 1) return 1;
if (memo[n] != -1) return memo[n];
return memo[n] = climbStairs(n - 1, memo) + climbStairs(n - 2, memo);
}

Tabulation
int climbStairs(int n) {
if (n <= 1) return 1;
int[] dp = new int[n + 1];
dp[0] = dp[1] = 1;
for (int i = 2; i <= n; i++) dp[i] = dp[i-1] + dp[i-2];
return dp[n];
}

2. Frog Jump (Min Cost)


Recursion
int frogJump(int n, int[] h) {
if (n == 0) return 0;
int left = frogJump(n - 1, h) + [Link](h[n] - h[n-1]);
int right = (n > 1) ? frogJump(n - 2, h) + [Link](h[n] - h[n-2])
: Integer.MAX_VALUE;
return [Link](left, right);
}

Memoization
int frogJump(int n, int[] h, int[] dp) {
if (n == 0) return 0;
if (dp[n] != -1) return dp[n];
int left = frogJump(n-1, h, dp) + [Link](h[n] - h[n-1]);
int right = (n > 1) ? frogJump(n-2, h, dp) + [Link](h[n] - h[n-2])
: Integer.MAX_VALUE;
return dp[n] = [Link](left, right);
}

Tabulation
int frogJump(int[] h) {
int n = [Link];
int[] dp = new int[n];
for (int i = 1; i < n; i++) {
int left = dp[i-1] + [Link](h[i] - h[i-1]);
int right = (i > 1) ? dp[i-2] + [Link](h[i] - h[i-2]) : Integer.MAX_VALUE;
dp[i] = [Link](left, right);
}
return dp[n - 1];
}

3. Frog Jump with K Distances


Memoization
int frogK(int n, int k, int[] h, int[] dp) {
if (n == 0) return 0;
if (dp[n] != -1) return dp[n];
int best = Integer.MAX_VALUE;
for (int j = 1; j <= k && n - j >= 0; j++) {
int cost = frogK(n - j, k, h, dp) + [Link](h[n] - h[n - j]);
best = [Link](best, cost);
}
return dp[n] = best;
}

Tabulation
int frogK(int[] h, int k) {
int n = [Link];
int[] dp = new int[n];
[Link](dp, Integer.MAX_VALUE); dp[0] = 0;
for (int i = 1; i < n; i++)
for (int j = 1; j <= k && i - j >= 0; j++)
dp[i] = [Link](dp[i], dp[i-j] + [Link](h[i] - h[i-j]));
return dp[n - 1];
}

4. Maximum Sum of Non-Adjacent Elements (House Robber I)


Recursion
int rob(int i, int[] nums) {
if (i < 0) return 0;
if (i == 0) return nums[0];
return [Link](nums[i] + rob(i - 2, nums), rob(i - 1, nums));
}

Memoization
int rob(int i, int[] nums, int[] dp) {
if (i < 0) return 0;
if (i == 0) return nums[0];
if (dp[i] != -1) return dp[i];
return dp[i] = [Link](nums[i] + rob(i-2, nums, dp), rob(i-1, nums, dp));
}

Tabulation
int rob(int[] nums) {
int n = [Link];
if (n == 1) return nums[0];
int[] dp = new int[n];
dp[0] = nums[0]; dp[1] = [Link](nums[0], nums[1]);
for (int i = 2; i < n; i++)
dp[i] = [Link](dp[i-1], nums[i] + dp[i-2]);
return dp[n - 1];
}

5. House Robber II (Circular)


Tabulation
int rob(int[] nums) {
int n = [Link];
if (n == 1) return nums[0];
return [Link](robRange(nums, 0, n-2), robRange(nums, 1, n-1));
}
int robRange(int[] nums, int lo, int hi) {
int prev2 = 0, prev1 = 0;
for (int i = lo; i <= hi; i++) {
int cur = [Link](prev1, nums[i] + prev2);
prev2 = prev1; prev1 = cur;
}
return prev1;
}

■ 2D Dynamic Programming

6. Ninja's Training
Memoization
int solve(int day, int last, int[][] pts, int[][] dp) {
if (day < 0) return 0;
if (dp[day][last] != -1) return dp[day][last];
int best = 0;
for (int task = 0; task < 3; task++)
if (task != last)
best = [Link](best, pts[day][task] + solve(day-1, task, pts, dp));
return dp[day][last] = best;
}

Tabulation
int ninjaTraining(int[][] pts) {
int n = [Link];
int[][] dp = new int[n][4];
dp[0][0] = [Link](pts[0][1], pts[0][2]);
dp[0][1] = [Link](pts[0][0], pts[0][2]);
dp[0][2] = [Link](pts[0][0], pts[0][1]);
dp[0][3] = [Link](pts[0][0], [Link](pts[0][1], pts[0][2]));
for (int day = 1; day < n; day++)
for (int last = 0; last < 4; last++)
for (int task = 0; task < 3; task++)
if (task != last)
dp[day][last] = [Link](dp[day][last], pts[day][task] + dp[day-1][task]);
return dp[n-1][3];
}

7. Unique Paths in a Grid


Recursion
int uniquePaths(int m, int n) {
if (m == 1 || n == 1) return 1;
return uniquePaths(m-1, n) + uniquePaths(m, n-1);
}

Memoization
int uniquePaths(int m, int n, int[][] dp) {
if (m == 1 || n == 1) return 1;
if (dp[m][n] != -1) return dp[m][n];
return dp[m][n] = uniquePaths(m-1, n, dp) + uniquePaths(m, n-1, dp);
}

Tabulation
int uniquePaths(int m, int n) {
int[][] dp = new int[m][n];
for (int[] row : dp) [Link](row, 1);
for (int i = 1; i < m; i++)
for (int j = 1; j < n; j++)
dp[i][j] = dp[i-1][j] + dp[i][j-1];
return dp[m-1][n-1];
}

8. Unique Paths II (with Obstacles)


Memoization
int solve(int i, int j, int[][] grid, int[][] dp) {
if (i < 0 || j < 0 || grid[i][j] == 1) return 0;
if (i == 0 && j == 0) return 1;
if (dp[i][j] != -1) return dp[i][j];
return dp[i][j] = solve(i-1, j, grid, dp) + solve(i, j-1, grid, dp);
}

Tabulation
int uniquePathsWithObstacles(int[][] grid) {
int m = [Link], n = grid[0].length;
int[][] dp = new int[m][n];
dp[0][0] = (grid[0][0] == 0) ? 1 : 0;
for (int i = 1; i < m; i++) dp[i][0] = (grid[i][0] == 0) ? dp[i-1][0] : 0;
for (int j = 1; j < n; j++) dp[0][j] = (grid[0][j] == 0) ? dp[0][j-1] : 0;
for (int i = 1; i < m; i++)
for (int j = 1; j < n; j++)
dp[i][j] = (grid[i][j] == 1) ? 0 : dp[i-1][j] + dp[i][j-1];
return dp[m-1][n-1];
}

9. Minimum Falling Path Sum


Memoization
int solve(int i, int j, int[][] mat, int[][] dp) {
if (j < 0 || j >= mat[0].length) return (int)1e9;
if (i == 0) return mat[0][j];
if (dp[i][j] != Integer.MAX_VALUE) return dp[i][j];
int up = solve(i-1, j, mat, dp);
int ul = solve(i-1, j-1, mat, dp);
int ur = solve(i-1, j+1, mat, dp);
return dp[i][j] = mat[i][j] + [Link](up, [Link](ul, ur));
}

Tabulation
int minFallingPathSum(int[][] mat) {
int n = [Link];
int[][] dp = new int[n][n];
dp[0] = mat[0].clone();
for (int i = 1; i < n; i++)
for (int j = 0; j < n; j++) {
int up = dp[i-1][j];
int ul = (j > 0) ? dp[i-1][j-1] : (int)1e9;
int ur = (j < n-1) ? dp[i-1][j+1] : (int)1e9;
dp[i][j] = mat[i][j] + [Link](up, [Link](ul, ur));
}
int ans = Integer.MAX_VALUE;
for (int v : dp[n-1]) ans = [Link](ans, v);
return ans;
}

10. Triangle (Min Path Sum)


Memoization
int solve(int i, int j, List<List<Integer>> tri, int[][] dp) {
int n = [Link]();
if (i == n-1) return [Link](i).get(j);
if (dp[i][j] != Integer.MAX_VALUE) return dp[i][j];
int down = solve(i+1, j, tri, dp);
int diag = solve(i+1, j+1, tri, dp);
return dp[i][j] = [Link](i).get(j) + [Link](down, diag);
}

Tabulation
int minimumTotal(List<List<Integer>> tri) {
int n = [Link]();
int[] dp = new ArrayList<>([Link](n-1)).stream()
.mapToInt(Integer::intValue).toArray();
for (int i = n-2; i >= 0; i--)
for (int j = 0; j <= i; j++)
dp[j] = [Link](i).get(j) + [Link](dp[j], dp[j+1]);
return dp[0];
}

11. Cherry Pickup II


Memoization
int solve(int r, int c1, int c2, int[][] g, int[][][] dp) {
int n = g[0].length;
if (c1 < 0 || c1 >= n || c2 < 0 || c2 >= n) return (int)-1e9;
if (r == [Link]) return 0;
if (dp[r][c1][c2] != -1) return dp[r][c1][c2];
int cherries = g[r][c1] + (c1 != c2 ? g[r][c2] : 0);
int best = (int)-1e9;
for (int d1 = -1; d1 <= 1; d1++)
for (int d2 = -1; d2 <= 1; d2++)
best = [Link](best, solve(r+1, c1+d1, c2+d2, g, dp));
return dp[r][c1][c2] = cherries + best;
}

Tabulation
int cherryPickup(int[][] g) {
int m = [Link], n = g[0].length;
int[][][] dp = new int[m][n][n];
for (int c1 = 0; c1 < n; c1++)
for (int c2 = 0; c2 < n; c2++)
dp[m-1][c1][c2] = g[m-1][c1] + (c1 != c2 ? g[m-1][c2] : 0);
for (int r = m-2; r >= 0; r--)
for (int c1 = 0; c1 < n; c1++)
for (int c2 = 0; c2 < n; c2++) {
int cherries = g[r][c1] + (c1 != c2 ? g[r][c2] : 0), best = (int)-1e9;
for (int d1 = -1; d1 <= 1; d1++)
for (int d2 = -1; d2 <= 1; d2++) {
int nc1 = c1+d1, nc2 = c2+d2;
if (nc1 >= 0 && nc1 < n && nc2 >= 0 && nc2 < n)
best = [Link](best, dp[r+1][nc1][nc2]);
}
dp[r][c1][c2] = cherries + best;
}
return dp[0][0][n-1];
}

■ DP on Stocks

12. Best Time to Buy and Sell Stock I


Tabulation / Greedy
int maxProfit(int[] prices) {
int minP = Integer.MAX_VALUE, ans = 0;
for (int p : prices) {
minP = [Link](minP, p);
ans = [Link](ans, p - minP);
}
return ans;
}

13. Best Time to Buy and Sell Stock II (Unlimited Transactions)


Memoization
int dp(int i, int buy, int[] prices, int[][] memo) {
if (i == [Link]) return 0;
if (memo[i][buy] != -1) return memo[i][buy];
if (buy == 1)
return memo[i][buy] = [Link](-prices[i] + dp(i+1, 0, prices, memo),
dp(i+1, 1, prices, memo));
return memo[i][buy] = [Link](prices[i] + dp(i+1, 1, prices, memo),
dp(i+1, 0, prices, memo));
}

Tabulation
int maxProfit(int[] prices) {
int n = [Link];
int[][] dp = new int[n+1][2];
for (int i = n-1; i >= 0; i--) {
dp[i][1] = [Link](-prices[i] + dp[i+1][0], dp[i+1][1]);
dp[i][0] = [Link](prices[i] + dp[i+1][1], dp[i+1][0]);
}
return dp[0][1];
}

14. Best Time to Buy and Sell Stock III (At Most 2 Transactions)
Memoization
int dp(int i, int buy, int cap, int[] p, int[][][] memo) {
if (i == [Link] || cap == 0) return 0;
if (memo[i][buy][cap] != -1) return memo[i][buy][cap];
if (buy == 1)
return memo[i][buy][cap] = [Link](-p[i] + dp(i+1,0,cap,p,memo),
dp(i+1,1,cap,p,memo));
return memo[i][buy][cap] = [Link](p[i] + dp(i+1,1,cap-1,p,memo),
dp(i+1,0,cap,p,memo));
}

Tabulation
int maxProfit(int[] prices) {
int n = [Link];
int[][][] dp = new int[n+1][2][3];
for (int i = n-1; i >= 0; i--)
for (int cap = 1; cap <= 2; cap++) {
dp[i][1][cap] = [Link](-prices[i]+dp[i+1][0][cap], dp[i+1][1][cap]);
dp[i][0][cap] = [Link](prices[i]+dp[i+1][1][cap-1], dp[i+1][0][cap]);
}
return dp[0][1][2];
}

15. Best Time to Buy and Sell Stock IV (At Most K Transactions)
Tabulation
int maxProfit(int k, int[] prices) {
int n = [Link];
int[][][] dp = new int[n+1][2][k+1];
for (int i = n-1; i >= 0; i--)
for (int cap = 1; cap <= k; cap++) {
dp[i][1][cap] = [Link](-prices[i]+dp[i+1][0][cap], dp[i+1][1][cap]);
dp[i][0][cap] = [Link](prices[i]+dp[i+1][1][cap-1], dp[i+1][0][cap]);
}
return dp[0][1][k];
}

16. Best Time to Buy and Sell Stock with Transaction Fee
Tabulation
int maxProfit(int[] prices, int fee) {
int n = [Link];
int[][] dp = new int[n+1][2];
for (int i = n-1; i >= 0; i--) {
dp[i][1] = [Link](-prices[i]+dp[i+1][0], dp[i+1][1]);
dp[i][0] = [Link](prices[i]-fee+dp[i+1][1], dp[i+1][0]);
}
return dp[0][1];
}

■ DP on Subsequences

17. Subset Sum Equals Target


Recursion
boolean subsetSum(int i, int target, int[] nums) {
if (target == 0) return true;
if (i == 0) return nums[0] == target;
boolean notTake = subsetSum(i-1, target, nums);
boolean take = (nums[i] <= target) && subsetSum(i-1, target-nums[i], nums);
return take || notTake;
}

Memoization
boolean solve(int i, int t, int[] nums, Boolean[][] dp) {
if (t == 0) return true;
if (i == 0) return nums[0] == t;
if (dp[i][t] != null) return dp[i][t];
boolean nt = solve(i-1, t, nums, dp);
boolean tk = (nums[i] <= t) && solve(i-1, t-nums[i], nums, dp);
return dp[i][t] = tk || nt;
}

Tabulation
boolean subsetSum(int[] nums, int target) {
int n = [Link];
boolean[][] dp = new boolean[n][target+1];
for (int i = 0; i < n; i++) dp[i][0] = true;
if (nums[0] <= target) dp[0][nums[0]] = true;
for (int i = 1; i < n; i++)
for (int t = 1; t <= target; t++)
dp[i][t] = dp[i-1][t] || (nums[i] <= t && dp[i-1][t-nums[i]]);
return dp[n-1][target];
}

18. Partition Equal Subset Sum


Tabulation
boolean canPartition(int[] nums) {
int sum = 0; for (int x : nums) sum += x;
if (sum % 2 != 0) return false;
int target = sum / 2, n = [Link];
boolean[][] dp = new boolean[n][target+1];
for (int i = 0; i < n; i++) dp[i][0] = true;
if (nums[0] <= target) dp[0][nums[0]] = true;
for (int i = 1; i < n; i++)
for (int t = 1; t <= target; t++)
dp[i][t] = dp[i-1][t] || (nums[i] <= t && dp[i-1][t-nums[i]]);
return dp[n-1][target];
}

19. Partition Into Two Subsets with Minimum Absolute Difference


Tabulation
int minimumDifference(int[] nums) {
int n = [Link], total = 0;
for (int x : nums) total += x;
int target = total / 2;
boolean[][] dp = new boolean[n][target+1];
for (int i = 0; i < n; i++) dp[i][0] = true;
if (nums[0] <= target) dp[0][nums[0]] = true;
for (int i = 1; i < n; i++)
for (int t = 1; t <= target; t++)
dp[i][t] = dp[i-1][t] || (nums[i] <= t && dp[i-1][t-nums[i]]);
for (int t = target; t >= 0; t--)
if (dp[n-1][t]) return [Link](total - 2*t);
return 0;
}

20. Count Subsets with Sum K


Tabulation
int countSubsets(int[] nums, int target) {
int n = [Link];
int[][] dp = new int[n][target+1];
for (int i = 0; i < n; i++) dp[i][0] = 1;
if (nums[0] <= target) dp[0][nums[0]] = 1;
for (int i = 1; i < n; i++)
for (int t = 0; t <= target; t++)
dp[i][t] = dp[i-1][t] + (nums[i] <= t ? dp[i-1][t-nums[i]] : 0);
return dp[n-1][target];
}

21. Count Partitions with Given Difference


Tabulation
int countPartitions(int[] nums, int d) {
int total = 0; for (int x : nums) total += x;
if ((total - d) < 0 || (total - d) % 2 != 0) return 0;
int target = (total - d) / 2, n = [Link];
int[][] dp = new int[n][target+1];
for (int i = 0; i < n; i++) dp[i][0] = 1;
if (nums[0] <= target) dp[0][nums[0]] += 1;
for (int i = 1; i < n; i++)
for (int t = 0; t <= target; t++)
dp[i][t] = dp[i-1][t] + (nums[i] <= t ? dp[i-1][t-nums[i]] : 0);
return dp[n-1][target];
}

22. 0/1 Knapsack


Recursion
int knapsack(int i, int W, int[] wt, int[] val) {
if (i == 0) return (wt[0] <= W) ? val[0] : 0;
int notTake = knapsack(i-1, W, wt, val);
int take = (wt[i] <= W) ? val[i] + knapsack(i-1, W-wt[i], wt, val) : 0;
return [Link](take, notTake);
}

Memoization
int knapsack(int i, int W, int[] wt, int[] val, int[][] dp) {
if (i == 0) return (wt[0] <= W) ? val[0] : 0;
if (dp[i][W] != -1) return dp[i][W];
int nt = knapsack(i-1, W, wt, val, dp);
int tk = (wt[i] <= W) ? val[i] + knapsack(i-1, W-wt[i], wt, val, dp) : 0;
return dp[i][W] = [Link](tk, nt);
}

Tabulation
int knapsack(int[] wt, int[] val, int W) {
int n = [Link];
int[][] dp = new int[n][W+1];
for (int w = wt[0]; w <= W; w++) dp[0][w] = val[0];
for (int i = 1; i < n; i++)
for (int w = 0; w <= W; w++) {
int nt = dp[i-1][w];
int tk = (wt[i] <= w) ? val[i] + dp[i-1][w-wt[i]] : 0;
dp[i][w] = [Link](tk, nt);
}
return dp[n-1][W];
}

23. Coin Change (Minimum Coins)


Recursion
int coinChange(int i, int amount, int[] coins) {
if (amount == 0) return 0;
if (i == 0) return (amount % coins[0] == 0) ? amount/coins[0] : (int)1e9;
int notTake = coinChange(i-1, amount, coins);
int take = (coins[i] <= amount)
? 1 + coinChange(i, amount - coins[i], coins) : (int)1e9;
return [Link](take, notTake);
}

Memoization
int coinChange(int i, int amt, int[] coins, int[][] dp) {
if (amt == 0) return 0;
if (i == 0) return (amt % coins[0] == 0) ? amt/coins[0] : (int)1e9;
if (dp[i][amt] != -1) return dp[i][amt];
int nt = coinChange(i-1, amt, coins, dp);
int tk = (coins[i] <= amt) ? 1 + coinChange(i, amt-coins[i], coins, dp) : (int)1e9;
return dp[i][amt] = [Link](tk, nt);
}

Tabulation
int coinChange(int[] coins, int amount) {
int n = [Link], INF = (int)1e9;
int[][] dp = new int[n][amount+1];
for (int a = 0; a <= amount; a++)
dp[0][a] = (a % coins[0] == 0) ? a/coins[0] : INF;
for (int i = 1; i < n; i++)
for (int a = 0; a <= amount; a++) {
int nt = dp[i-1][a];
int tk = (coins[i] <= a) ? 1 + dp[i][a-coins[i]] : INF;
dp[i][a] = [Link](tk, nt);
}
return dp[n-1][amount] >= INF ? -1 : dp[n-1][amount];
}
24. Target Sum
Memoization
int dp(int i, int cur, int target, int[] nums, HashMap<String,Integer> memo) {
if (i == [Link]) return cur == target ? 1 : 0;
String key = i + "," + cur;
if ([Link](key)) return [Link](key);
int res = dp(i+1, cur+nums[i], target, nums, memo)
+ dp(i+1, cur-nums[i], target, nums, memo);
[Link](key, res); return res;
}

Tabulation (Subset Count)


int findTargetSumWays(int[] nums, int target) {
int total = 0; for (int x : nums) total += x;
if ((total + target) % 2 != 0 || [Link](target) > total) return 0;
int s = (total + target) / 2, n = [Link];
int[][] dp = new int[n+1][s+1]; dp[0][0] = 1;
for (int i = 1; i <= n; i++)
for (int t = 0; t <= s; t++)
dp[i][t] = dp[i-1][t] + (nums[i-1] <= t ? dp[i-1][t-nums[i-1]] : 0);
return dp[n][s];
}

25. Coin Change II (Count Ways)


Memoization
int dp(int i, int amount, int[] coins, int[][] memo) {
if (amount == 0) return 1;
if (i == 0) return (amount % coins[0] == 0) ? 1 : 0;
if (memo[i][amount] != -1) return memo[i][amount];
int nt = dp(i-1, amount, coins, memo);
int tk = (coins[i] <= amount) ? dp(i, amount-coins[i], coins, memo) : 0;
return memo[i][amount] = tk + nt;
}

Tabulation
int change(int amount, int[] coins) {
int n = [Link];
int[][] dp = new int[n][amount+1];
for (int a = 0; a <= amount; a++) dp[0][a] = (a % coins[0] == 0) ? 1 : 0;
for (int i = 1; i < n; i++)
for (int a = 0; a <= amount; a++)
dp[i][a] = dp[i-1][a] + (coins[i] <= a ? dp[i][a-coins[i]] : 0);
return dp[n-1][amount];
}

26. Unbounded Knapsack


Memoization
int dp(int i, int W, int[] wt, int[] val, int[][] memo) {
if (i == 0) return (W / wt[0]) * val[0];
if (memo[i][W] != -1) return memo[i][W];
int nt = dp(i-1, W, wt, val, memo);
int tk = (wt[i] <= W) ? val[i] + dp(i, W-wt[i], wt, val, memo) : 0;
return memo[i][W] = [Link](tk, nt);
}

Tabulation
int unboundedKnapsack(int[] wt, int[] val, int W) {
int n = [Link];
int[][] dp = new int[n][W+1];
for (int w = 0; w <= W; w++) dp[0][w] = (w / wt[0]) * val[0];
for (int i = 1; i < n; i++)
for (int w = 0; w <= W; w++) {
int nt = dp[i-1][w];
int tk = (wt[i] <= w) ? val[i] + dp[i][w-wt[i]] : 0;
dp[i][w] = [Link](tk, nt);
}
return dp[n-1][W];
}

27. Rod Cutting Problem


Memoization
int dp(int i, int length, int[] price, int[][] memo) {
if (i == 0) return length * price[0];
if (memo[i][length] != -1) return memo[i][length];
int nt = dp(i-1, length, price, memo);
int tk = ((i+1) <= length) ? price[i] + dp(i, length-(i+1), price, memo) : 0;
return memo[i][length] = [Link](tk, nt);
}

Tabulation
int rodCutting(int[] price, int n) {
int[][] dp = new int[n][n+1];
for (int len = 0; len <= n; len++) dp[0][len] = len * price[0];
for (int i = 1; i < n; i++)
for (int len = 0; len <= n; len++) {
int nt = dp[i-1][len];
int tk = ((i+1) <= len) ? price[i] + dp[i][len-(i+1)] : 0;
dp[i][len] = [Link](tk, nt);
}
return dp[n-1][n];
}

■ LIS — Longest Increasing Subsequence Variants

28. Longest Increasing Subsequence (LIS)


Recursion
int solve(int i, int prev, int[] nums) {
if (i == [Link]) return 0;
int take = 0;
if (prev == -1 || nums[i] > nums[prev])
take = 1 + solve(i+1, i, nums);
return [Link](take, solve(i+1, prev, nums));
}

Memoization
int dp(int i, int prev, int[] nums, int[][] memo) {
if (i == [Link]) return 0;
if (memo[i][prev+1] != -1) return memo[i][prev+1];
int take = 0;
if (prev == -1 || nums[i] > nums[prev])
take = 1 + dp(i+1, i, nums, memo);
return memo[i][prev+1] = [Link](take, dp(i+1, prev, nums, memo));
}

Tabulation
int lengthOfLIS(int[] nums) {
int n = [Link], ans = 1;
int[] dp = new int[n]; [Link](dp, 1);
for (int i = 1; i < n; i++) {
for (int j = 0; j < i; j++)
if (nums[j] < nums[i]) dp[i] = [Link](dp[i], dp[j]+1);
ans = [Link](ans, dp[i]);
}
return ans;
}

29. Print LIS


Tabulation + Backtrack
List<Integer> printLIS(int[] nums) {
int n = [Link];
int[] dp = new int[n], parent = new int[n];
[Link](dp, 1); [Link](parent, -1);
int maxLen = 1, idx = 0;
for (int i = 1; i < n; i++) {
for (int j = 0; j < i; j++)
if (nums[j] < nums[i] && dp[j]+1 > dp[i]) { dp[i]=dp[j]+1; parent[i]=j; }
if (dp[i] > maxLen) { maxLen = dp[i]; idx = i; }
}
List<Integer> lis = new ArrayList<>();
while (idx != -1) { [Link](nums[idx]); idx = parent[idx]; }
[Link](lis); return lis;
}

30. Largest Divisible Subset


Tabulation
List<Integer> largestDivisibleSubset(int[] nums) {
[Link](nums); int n = [Link];
int[] dp = new int[n], parent = new int[n];
[Link](dp, 1); [Link](parent, -1);
int maxLen = 1, idx = 0;
for (int i = 1; i < n; i++) {
for (int j = 0; j < i; j++)
if (nums[i] % nums[j] == 0 && dp[j]+1 > dp[i]) { dp[i]=dp[j]+1; parent[i]=j; }
if (dp[i] > maxLen) { maxLen = dp[i]; idx = i; }
}
List<Integer> res = new ArrayList<>();
while (idx != -1) { [Link](nums[idx]); idx = parent[idx]; }
[Link](res); return res;
}

31. Longest String Chain


Tabulation
int longestStrChain(String[] words) {
[Link](words, [Link](String::length));
Map<String,Integer> dp = new HashMap<>();
int ans = 1;
for (String w : words) {
[Link](w, 1);
for (int i = 0; i < [Link](); i++) {
String pred = [Link](0,i) + [Link](i+1);
if ([Link](pred))
[Link](w, [Link]([Link](w), [Link](pred)+1));
}
ans = [Link](ans, [Link](w));
}
return ans;
}

32. Longest Bitonic Subsequence


Tabulation
int longestBitonicSubsequence(int[] nums) {
int n = [Link];
int[] inc = new int[n], dec = new int[n];
[Link](inc, 1); [Link](dec, 1);
for (int i = 1; i < n; i++)
for (int j = 0; j < i; j++)
if (nums[j] < nums[i]) inc[i] = [Link](inc[i], inc[j]+1);
for (int i = n-2; i >= 0; i--)
for (int j = i+1; j < n; j++)
if (nums[j] < nums[i]) dec[i] = [Link](dec[i], dec[j]+1);
int ans = 0;
for (int i = 0; i < n; i++) ans = [Link](ans, inc[i]+dec[i]-1);
return ans;
}

33. Number of Longest Increasing Subsequences


Tabulation
int findNumberOfLIS(int[] nums) {
int n = [Link];
int[] dp = new int[n], cnt = new int[n];
[Link](dp, 1); [Link](cnt, 1);
int maxLen = 1;
for (int i = 1; i < n; i++) {
for (int j = 0; j < i; j++) {
if (nums[j] < nums[i]) {
if (dp[j]+1 > dp[i]) { dp[i]=dp[j]+1; cnt[i]=cnt[j]; }
else if (dp[j]+1 == dp[i]) cnt[i] += cnt[j];
}
}
maxLen = [Link](maxLen, dp[i]);
}
int res = 0;
for (int i = 0; i < n; i++) if (dp[i] == maxLen) res += cnt[i];
return res;
}

■ DP on Strings

34. Longest Common Subsequence (LCS)


Recursion
int lcs(int i, int j, String s1, String s2) {
if (i < 0 || j < 0) return 0;
if ([Link](i) == [Link](j)) return 1 + lcs(i-1, j-1, s1, s2);
return [Link](lcs(i-1, j, s1, s2), lcs(i, j-1, s1, s2));
}

Memoization
int dp(int i, int j, String s1, String s2, int[][] memo) {
if (i < 0 || j < 0) return 0;
if (memo[i][j] != -1) return memo[i][j];
if ([Link](i) == [Link](j)) return memo[i][j] = 1 + dp(i-1,j-1,s1,s2,memo);
return memo[i][j] = [Link](dp(i-1,j,s1,s2,memo), dp(i,j-1,s1,s2,memo));
}

Tabulation
int lcs(String s1, String s2) {
int m = [Link](), n = [Link]();
int[][] dp = new int[m+1][n+1];
for (int i = 1; i <= m; i++)
for (int j = 1; j <= n; j++)
if ([Link](i-1) == [Link](j-1)) dp[i][j] = 1 + dp[i-1][j-1];
else dp[i][j] = [Link](dp[i-1][j], dp[i][j-1]);
return dp[m][n];
}

35. Longest Common Substring


Tabulation
int longestCommonSubstring(String s1, String s2) {
int m = [Link](), n = [Link](), ans = 0;
int[][] dp = new int[m+1][n+1];
for (int i = 1; i <= m; i++)
for (int j = 1; j <= n; j++) {
if ([Link](i-1) == [Link](j-1))
ans = [Link](ans, dp[i][j] = 1 + dp[i-1][j-1]);
else dp[i][j] = 0;
}
return ans;
}

36. Longest Palindromic Subsequence


Tabulation (via LCS with reverse)
int longestPalindromeSubseq(String s) {
String t = new StringBuilder(s).reverse().toString();
int n = [Link]();
int[][] dp = new int[n+1][n+1];
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
if ([Link](i-1) == [Link](j-1)) dp[i][j] = 1 + dp[i-1][j-1];
else dp[i][j] = [Link](dp[i-1][j], dp[i][j-1]);
return dp[n][n];
}

37. Minimum Insertions to Make String Palindrome


Tabulation
int minInsertions(String s) {
int n = [Link]();
// answer = n - LPS
String t = new StringBuilder(s).reverse().toString();
int[][] dp = new int[n+1][n+1];
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
if ([Link](i-1) == [Link](j-1)) dp[i][j] = 1 + dp[i-1][j-1];
else dp[i][j] = [Link](dp[i-1][j], dp[i][j-1]);
return n - dp[n][n];
}

38. Min Insertions/Deletions to Convert String A to B


Tabulation
int minDistance(String s1, String s2) {
int m = [Link](), n = [Link]();
int[][] dp = new int[m+1][n+1];
for (int i = 1; i <= m; i++)
for (int j = 1; j <= n; j++)
if ([Link](i-1) == [Link](j-1)) dp[i][j] = 1 + dp[i-1][j-1];
else dp[i][j] = [Link](dp[i-1][j], dp[i][j-1]);
int lcs = dp[m][n];
return (m - lcs) + (n - lcs);
}
39. Shortest Common Supersequence
Tabulation
String shortestCommonSupersequence(String s1, String s2) {
int m=[Link](), n=[Link]();
int[][] dp = new int[m+1][n+1];
for(int i=1;i<=m;i++) for(int j=1;j<=n;j++)
if([Link](i-1)==[Link](j-1)) dp[i][j]=1+dp[i-1][j-1];
else dp[i][j]=[Link](dp[i-1][j],dp[i][j-1]);
StringBuilder sb = new StringBuilder();
int i=m, j=n;
while(i>0 && j>0) {
if([Link](i-1)==[Link](j-1)) { [Link]([Link](i-1)); i--; j--; }
else if(dp[i-1][j] > dp[i][j-1]) [Link]([Link](--i));
else [Link]([Link](--j));
}
while(i>0) [Link]([Link](--i));
while(j>0) [Link]([Link](--j));
return [Link]().toString();
}

40. Distinct Subsequences


Memoization
long dp(int i, int j, String s, String t, long[][] memo) {
if (j < 0) return 1;
if (i < 0) return 0;
if (memo[i][j] != -1) return memo[i][j];
if ([Link](i) == [Link](j))
return memo[i][j] = dp(i-1,j-1,s,t,memo) + dp(i-1,j,s,t,memo);
return memo[i][j] = dp(i-1,j,s,t,memo);
}

Tabulation
int numDistinct(String s, String t) {
int m=[Link](), n=[Link]();
long[][] dp = new long[m+1][n+1];
for (int i=0;i<=m;i++) dp[i][0]=1;
for (int i=1;i<=m;i++)
for (int j=1;j<=n;j++) {
dp[i][j]=dp[i-1][j];
if ([Link](i-1)==[Link](j-1)) dp[i][j]+=dp[i-1][j-1];
}
return (int)dp[m][n];
}

41. Edit Distance


Recursion
int minDist(int i, int j, String s1, String s2) {
if (i < 0) return j + 1;
if (j < 0) return i + 1;
if ([Link](i) == [Link](j)) return minDist(i-1, j-1, s1, s2);
return 1 + [Link](minDist(i-1, j, s1, s2),
[Link](minDist(i, j-1, s1, s2), minDist(i-1, j-1, s1, s2)));
}

Memoization
int dp(int i, int j, String s1, String s2, int[][] memo) {
if (i < 0) return j+1; if (j < 0) return i+1;
if (memo[i][j] != -1) return memo[i][j];
if ([Link](i) == [Link](j)) return memo[i][j] = dp(i-1,j-1,s1,s2,memo);
return memo[i][j] = 1 + [Link](dp(i-1,j,s1,s2,memo),
[Link](dp(i,j-1,s1,s2,memo), dp(i-1,j-1,s1,s2,memo)));
}
Tabulation
int minDistance(String s1, String s2) {
int m=[Link](), n=[Link]();
int[][] dp = new int[m+1][n+1];
for (int i=0;i<=m;i++) dp[i][0]=i;
for (int j=0;j<=n;j++) dp[0][j]=j;
for (int i=1;i<=m;i++)
for (int j=1;j<=n;j++)
if ([Link](i-1)==[Link](j-1)) dp[i][j]=dp[i-1][j-1];
else dp[i][j]=1+[Link](dp[i-1][j], [Link](dp[i][j-1],dp[i-1][j-1]));
return dp[m][n];
}

42. Wildcard Matching


Memoization
boolean dp(int i, int j, String s, String p, Boolean[][] memo) {
if (i < 0 && j < 0) return true;
if (j < 0) return false;
if (i < 0) { for(int k=0;k<=j;k++) if([Link](k)!='*') return false; return true; }
if (memo[i][j] != null) return memo[i][j];
if ([Link](j) == '*') return memo[i][j] = dp(i-1,j,s,p,memo)||dp(i,j-1,s,p,memo);
if ([Link](j)=='?' || [Link](i)==[Link](j)) return memo[i][j]=dp(i-1,j-1,s,p,memo);
return memo[i][j] = false;
}

Tabulation
boolean isMatch(String s, String p) {
int m=[Link](), n=[Link]();
boolean[][] dp = new boolean[m+1][n+1]; dp[0][0]=true;
for (int j=1;j<=n;j++) dp[0][j] = dp[0][j-1] && [Link](j-1)=='*';
for (int i=1;i<=m;i++)
for (int j=1;j<=n;j++)
if ([Link](j-1)=='*') dp[i][j]=dp[i-1][j]||dp[i][j-1];
else if ([Link](j-1)=='?'||[Link](i-1)==[Link](j-1)) dp[i][j]=dp[i-1][j-1];
return dp[m][n];
}

43. Longest Palindromic Substring


Tabulation (Expand Around Center)
String longestPalindrome(String s) {
int n=[Link](), start=0, maxLen=1;
boolean[][] dp = new boolean[n][n];
for (int i=0;i<n;i++) dp[i][i]=true;
for (int len=2;len<=n;len++)
for (int i=0;i<=n-len;i++) {
int j=i+len-1;
if (len==2) dp[i][j]=([Link](i)==[Link](j));
else dp[i][j]=([Link](i)==[Link](j) && dp[i+1][j-1]);
if (dp[i][j] && len>maxLen) { maxLen=len; start=i; }
}
return [Link](start, start+maxLen);
}

■ MCM — Matrix Chain Multiplication & Partition DP

44. Matrix Chain Multiplication


Recursion
int mcm(int i, int j, int[] arr) {
if (i >= j) return 0;
int best = Integer.MAX_VALUE;
for (int k = i; k < j; k++) {
int cost = arr[i-1]*arr[k]*arr[j] + mcm(i,k,arr) + mcm(k+1,j,arr);
best = [Link](best, cost);
}
return best;
}

Memoization
int dp(int i, int j, int[] arr, int[][] memo) {
if (i >= j) return 0;
if (memo[i][j] != -1) return memo[i][j];
int best = Integer.MAX_VALUE;
for (int k = i; k < j; k++) {
int cost = arr[i-1]*arr[k]*arr[j] + dp(i,k,arr,memo) + dp(k+1,j,arr,memo);
best = [Link](best, cost);
}
return memo[i][j] = best;
}

Tabulation
int mcm(int[] arr) {
int n = [Link] - 1;
int[][] dp = new int[n+1][n+1];
for (int len = 2; len <= n; len++)
for (int i = 1; i <= n-len+1; i++) {
int j = i+len-1; dp[i][j] = Integer.MAX_VALUE;
for (int k = i; k < j; k++) {
int cost = arr[i-1]*arr[k]*arr[j]+dp[i][k]+dp[k+1][j];
dp[i][j] = [Link](dp[i][j], cost);
}
}
return dp[1][n];
}

45. Minimum Cost to Cut the Stick


Memoization
int dp(int i, int j, int[] cuts, int[][] memo) {
if (j - i <= 1) return 0;
if (memo[i][j] != -1) return memo[i][j];
int best = Integer.MAX_VALUE;
for (int k = i+1; k < j; k++)
best = [Link](best, cuts[j]-cuts[i] + dp(i,k,cuts,memo) + dp(k,j,cuts,memo));
return memo[i][j] = best;
}
// Call: add 0 and n to cuts array, sort it

Tabulation
int minCost(int n, int[] cuts) {
int[] c = [Link](cuts, [Link]+2);
c[[Link]]=0; c[[Link]+1]=n;
[Link](c); int m=[Link];
int[][] dp = new int[m][m];
for (int len=2;len<m;len++)
for (int i=0;i<m-len;i++) {
int j=i+len; dp[i][j]=Integer.MAX_VALUE;
for (int k=i+1;k<j;k++)
dp[i][j]=[Link](dp[i][j], c[j]-c[i]+dp[i][k]+dp[k][j]);
}
return dp[0][m-1];
}
46. Burst Balloons
Memoization
int dp(int i, int j, int[] nums, int[][] memo) {
if (i > j) return 0;
if (memo[i][j] != -1) return memo[i][j];
int best = 0;
for (int k = i; k <= j; k++) {
int coins = nums[i-1]*nums[k]*nums[j+1] + dp(i,k-1,nums,memo) + dp(k+1,j,nums,memo);
best = [Link](best, coins);
}
return memo[i][j] = best;
}
// Pad nums with 1 at both ends before calling dp(1, n, nums, memo)

Tabulation
int maxCoins(int[] balloons) {
int n = [Link];
int[] nums = new int[n+2]; nums[0]=nums[n+1]=1;
for (int i=0;i<n;i++) nums[i+1]=balloons[i];
int[][] dp = new int[n+2][n+2];
for (int len=1;len<=n;len++)
for (int i=1;i<=n-len+1;i++) {
int j=i+len-1;
for (int k=i;k<=j;k++)
dp[i][j]=[Link](dp[i][j], nums[i-1]*nums[k]*nums[j+1]
+(k>i?dp[i][k-1]:0)+(k<j?dp[k+1][j]:0));
}
return dp[1][n];
}

47. Palindrome Partitioning II (Min Cuts)


Memoization
boolean isPalin(int i, int j, String s) {
while (i < j) { if ([Link](i++) != [Link](j--)) return false; } return true;
}
int dp(int i, String s, int[] memo) {
if (i == [Link]()) return 0;
if (memo[i] != -1) return memo[i];
int best = Integer.MAX_VALUE;
for (int j = i; j < [Link](); j++)
if (isPalin(i, j, s)) best = [Link](best, 1 + dp(j+1, s, memo));
return memo[i] = best;
}

Tabulation
int minCut(String s) {
int n = [Link]();
boolean[][] palin = new boolean[n][n];
for (int i=n-1;i>=0;i--)
for (int j=i;j<n;j++)
palin[i][j]=([Link](i)==[Link](j)) && (j-i<=2||palin[i+1][j-1]);
int[] dp = new int[n+1];
for (int i=n-1;i>=0;i--) {
dp[i]=n;
for (int j=i;j<n;j++)
if (palin[i][j]) dp[i]=[Link](dp[i], 1+dp[j+1]);
}
return dp[0]-1;
}
All 47 problems from the TUF+ DSA Checklist DP sheet are covered above in Java. Space-optimised O(n)
variants can be derived from tabulation by keeping only 1-2 rows.

You might also like