java - 使用动态规划删除中间元素的三重乘积的最小总和

标签 java algorithm dynamic-programming combinatorics

我已经给出了 N 个数字的序列 (4 ≤ N ≤ 150)。选择一个索引 i (0 < i < N) 并与左右数相乘,换句话说,与 i-1 和 i+1 相乘。然后删除第 i 个数字。这样做直到序列只剩下两个数字。目标是找到这些产品的最小总和,这显然取决于选择指数的顺序。

例如对于序列 44、45、5、39、15、22、10,最小的总和为 17775 按以下顺序使用索引:1->3->4->5->2 这是总和: 44*45*5 + 5*39*15 + 5*15*22 + 5*22*10 + 44*5*10 = 9900 + 2925 + 1650 + 1100 + 2200 = 17775

我找到了一个使用递归函数的解决方案:

public static int smallestSum(List<Integer> values) {
    if (values.size() == 3)
        return values.get(0) * values.get(1) * values.get(2);
    else {
        int ret = Integer.MAX_VALUE;

        for (int i = 1; i < values.size() - 1; i++) {
            List<Integer> copy = new ArrayList<Integer>(values);
            copy.remove(i);

            int val = smallestSum(copy) + values.get(i - 1) * values.get(i) * values.get(i + 1);
            if (val < ret) ret = val; 
        }

        return ret;
    }
}

但是,这种解决方案只适用于小 N,而不适用于更大数量的数字。我正在寻找的是一种使用迭代动态编程方法来执行此操作的方法。

最佳答案

DP 所需的最佳子结构是,给定最后删除的元素的标识,左侧元素的消除策略独立于右侧元素的消除策略。这是一个新的递归函数(smallestSumA,以及问题的版本和比较两者的测试工具)结合了这一观察:

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class Foo {
  public static void main(String[] args) {
    Random r = new Random();
    for (int i = 0; i < 10000; i++) {
      List<Integer> values = new ArrayList<Integer>();
      for (int n = 3 + r.nextInt(8); n > 0; n--) {
        values.add(r.nextInt(100));
      }
      int a = smallestSumA(values, 0, values.size() - 1);
      int q = smallestSumQ(values);
      if (q != a) {
        System.err.println("oops");
        System.err.println(q);
        System.err.println(a);
        System.err.println(values);
      }
    }
  }

  public static int smallestSumA(List<Integer> values, int first, int last) {
    if (first + 2 > last)
      return 0;
    int ret = Integer.MAX_VALUE;
    for (int i = first + 1; i <= last - 1; i++) {
      int val = (smallestSumA(values, first, i)
          + values.get(first) * values.get(i) * values.get(last) + smallestSumA(values, i, last));
      if (val < ret)
        ret = val;
    }
    return ret;
  }

  public static int smallestSumQ(List<Integer> values) {
    if (values.size() == 3)
      return values.get(0) * values.get(1) * values.get(2);
    else {
      int ret = Integer.MAX_VALUE;

      for (int i = 1; i < values.size() - 1; i++) {
        List<Integer> copy = new ArrayList<Integer>(values);
        copy.remove(i);

        int val = smallestSumQ(copy) + values.get(i - 1) * values.get(i) * values.get(i + 1);
        if (val < ret)
          ret = val;
      }

      return ret;
    }
  }
}

调用为 smallestSum(values, 0, values.size() - 1)

要获得 DP,请观察 N choose 2 firstlast 的不同设置,然后内存。运行时间为 O(N^3)

关于java - 使用动态规划删除中间元素的三重乘积的最小总和,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41469118/

相关文章:

algorithm - 子串算法

Java:插入排序算法交换

algorithm - 史密斯沃特曼算法如何将较短的序列与较长的序列匹配

python - Python Gekko 中目标函数的约束

c++ - 构建分数面试挑战

java - 使用 Java 将记录插入 Berkeley DB

java - SSL 有两种方式不适用于我的 OpenLDAP

javascript - 无法将 Javascript 对象转换为 Java 模型

java - 如何在 GWT 中居中弹出/对话框窗口?

java - 检测数组中的所有突然变化