动态编程问题中的最佳路径

时间:2019-09-27 20:11:12

标签: algorithm dynamic-programming mathematical-optimization

我正在尝试找出如何为可以通过动态编程解决的问题找到最佳路径。我对我们尝试优化空间的情况很感兴趣。

为了更好地解释我的问题,让我们考虑背包问题。


有3个项目,如下所示:

        I1      I2      I3
---------------------------
Val     5       4       3     

Weight  4       5       2

最佳路径是为最佳解决方案应选择的项目。

重复关系如下:

Let n be the nth item 
let c be the remaining capacity in the knapsack

f(n, c) = 0             // if n=0
f(n, c) = f(n-1, c)     // if weight[n] > c
f(n, c) = max(f(n-1, c), value[n] + f(n-1, c-weight[n]))  // if weight[n] <= c

我已经基于这种递归关系(在Java中)编写了一个DP解决方案,而没有进行如下空间优化:

public static void main(String[] args) {
    int[] value = {5, 4, 3};
    int[] weight = {4, 5, 2};

    int capacity = 9;


    int[][] dp = new int[value.length+1][capacity+1];

    for(int i=0; i<=value.length; i++) {
        for(int j=0; j<=capacity; j++) {
            if(i==0) {
                dp[i][j] = 0;
            } else {
                if(weight[i-1] <= j){
                    dp[i][j] = Math.max(dp[i-1][j], value[i-1] + dp[i-1][j - weight[i-1] ]);
                } else {
                    dp[i][j] = dp[i-1][j];
                }
            }
        }
    }

    System.out.println("optimal value is: " + dp[value.length][capacity]);
}

这将打印出最优解为9。

我现在要查找构成最佳解决方案的项目(在本例中为I1,I2)。

我使用的逻辑如下:

  1. 矩阵dp [] []如下:

    0 0 0 0 0 0 0 0 0 0
    0 0 0 0 5 5 5 5 5 5
    0 0 0 0 5 5 5 5 5 9
    0 0 3 3 5 5 8 8 8 9

  2. dp [] []中的
  3. 第4行(索引3)对应于项目3,因此我将dp [3] [9](右下角)与dp [2] [9]进行了比较。由于两个值相同,因此我知道未选择项目3 。我去dp [2] [9]。

  4. 我比较dp [2] [9]和dp [1] [9]。由于值不同,我知道选择了项目2 。我去dp [1] [9-项目2的重量] => dp [1] [4]。 我比较dp [1] [4]和dp [0] [4]。值是不同的,所以我知道选择了项目1 。我去dp [0] [4-项目1的重量] => dp [0] [0]。
  5. dp [0] [0]是终端状态,所以我返回。

此操作的结果是:[1,1,0]其中1表示item1,采用item2,0表示未采用item3。


我的问题是:

优化空间时,如何找到路径(在这种情况下为挑选的物品)?甚至有可能吗?

例如,我可以使用2个数组而不是使用矩阵来更改程序,如下所示:

public static void main(String[] args) {
    int[] value = {5, 4, 3};
    int[] weight = {4, 5, 2};

    int capacity = 9;

    int[] row0 = new int[capacity+1];
    int[] row1 = new int[capacity+1];
    for(int i=0; i<=3; i++) {
        for(int j=0; j<=capacity; j++) {
            if(i==0) {
                row1[j] = 0;
            } else {
                if(weight[i-1] <= j) {
                    row1[j] = Math.max(row0[j], value[i-1]+ row0[j-weight[i-1]]);
                } else {
                    row1[j] = row0[j];
                }
            }
        }
        for(int j = 0; j< row0.length; j++)
            row0[j] = row1[j];
    }

    System.out.println("optimal value is: " + row1[capacity]);

}

如果这样做,最多只能有最后两行:

row0 = { 0 0 0 0 5 5 5 5 5 9 }
row1 = { 0 0 3 3 5 5 8 8 8 9 }

如何仅使用此信息来追溯路径?

1 个答案:

答案 0 :(得分:1)

没有所有DP问题的好解决方案。

例如,对于这个问题,我将使用每个可访问的和保留一个位掩码,以指示选择要生成该和的元素。这适用于背包,因为元素的数量很少,选择顺序无关紧要。

对于许多其他DP问题(例如,LCS或最短路径),可以很好地将路径记住为反向链接列表。列表有尾巴,通常您必须记住的那些都有相似的历史。您可能经常需要扫描结构以确保其仍然紧凑。确实需要时,可以删除第N个元素,然后在重建路径时需要进行一次小的搜索以连接每对元素。