Rod Cutting

There is a rod of length N lying on x-axis with its left end at x = 0 and right end at x = N. Now, there are M weak points on this rod denoted by positive integer values(all less than N) A1, A2, …, AM. You have to cut rod at all these weak points. You can perform these cuts in any order. After a cut, rod gets divided into two smaller sub-rods. Cost of making a cut is the length of the sub-rod in which you are making a cut.

Your aim is to minimise this cost. Return an array denoting the sequence in which you will make cuts. If two different sequences of cuts give same cost, return the lexicographically smallest.

Notes:

For example,

N = 6
A = [1, 2, 5]

If we make cuts in order [1, 2, 5], let us see what total cost would be.
For first cut, the length of rod is 6.
For second cut, the length of sub-rod in which we are making cut is 5(since we already have made a cut at 1).
For third cut, the length of sub-rod in which we are making cut is 4(since we already have made a cut at 2).
So, total cost is 6 + 5 + 4.

Cut order          | Sum of cost
(lexicographically | of each cut
 sorted)           |
___________________|_______________
[1, 2, 5]          | 6 + 5 + 4 = 15
[1, 5, 2]          | 6 + 5 + 4 = 15
[2, 1, 5]          | 6 + 2 + 4 = 12
[2, 5, 1]          | 6 + 4 + 2 = 12
[5, 1, 2]          | 6 + 5 + 4 = 15
[5, 2, 1]          | 6 + 5 + 2 = 13


So, we return [2, 1, 5].


Solution:

Time/Space: O(n^3)

public class Solution {
    // dp[i][j] = min cost of cutting B[i] to B[j]
    // dp[i][j] = min(dp[i][k] + dp[k + 1][j] + B[j] - B[i])
    // dp[0][0] = 0
    // dp[0][n - 1]
    public int[] rodCut(int A, int[] B) {
        int n = B.length;
        if (n == 0) return new int[0];
        int[] cuts = new int[n + 2];
        cuts[n + 1] = A; 
        for (int i = 1; i <= B.length; i ++) {
            cuts[i] = B[i - 1];
        }
        n = cuts.length;
        long[][] dp = new long[n][n];
        List<Integer>[][] paths = new ArrayList[n][n];
        for (int len = 0; len < n; len ++) {
            for (int i = 0; i < n; i ++) {
                int j = i + len;
                if (j < n && i + 1 == j) {
                    dp[i][j] = 0;
                } else if (j < n) {
                    dp[i][j] = Long.MAX_VALUE;
                    paths[i][j] = new ArrayList<>();
                    for (int k = i + 1; k < j; k ++) {
                        if (dp[i][k] + dp[k][j] + cuts[j] - cuts[i] < dp[i][j]) {
                            paths[i][j].clear();
                            dp[i][j] = Math.min(dp[i][j], dp[i][k] + dp[k][j] + cuts[j] - cuts[i]);
                            if (paths[k][j] != null) paths[i][j].addAll(paths[k][j]);
                            if (paths[i][k] != null) paths[i][j].addAll(paths[i][k]);
                            paths[i][j].add(cuts[k]); 
                        }
                    }
                    
                }
            }
        }
        // for (List<Integer>[] a: paths) {
        //     for (List<Integer> b : a) {
        //         if (b != null) System.out.println(Arrays.toString(b.toArray()));
        //     }
        // }
        // System.out.println(Arrays.toString(paths[0][n - 1].toArray()));
        int[] result = new int[B.length];
        List<Integer> path = paths[0][n - 1];
        Collections.reverse(path);
        for (int i = 0; i < result.length; i ++) {
            result[i] = path.get(i);
        }
        return result;
    }
}