使用动态编程删除中间元素的三元组产品的最小总和

时间:2017-01-04 16:47:31

标签: java algorithm dynamic-programming combinatorics

我给出了一系列N个数(4≤N≤150)。挑选一个索引i(0

E.g。对于序列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但不适用于更大数量的数字。我正在寻找的是使用迭代动态编程方法来实现这一目的。

2 个答案:

答案 0 :(得分:3)

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 2first只有last个不同的设置,并进行记忆。运行时间为O(N^3)

答案 1 :(得分:0)

如果有人对DP解决方案感兴趣,基于David Eisenstat的递归解决方案,这里是一个使用DP的迭代解决方案(对于许多大数字来说,用long替换int&#39; s很有用#39; S):

public static int smallestSum(List<Integer> values) {
    int[][] table = new int[values.size()][values.size()];

    for (int i = 2; i < values.size(); i++) {
        for (int j = 0; j + i < values.size(); j++) {
            int ret = Integer.MAX_VALUE;

            for (int k = j + 1; k <= j + i - 1; k++) {
                int val = table[j][k] + values.get(j) * values.get(k) * values.get(j + i) + table[k][j + i];
                if (val < ret) ret = val;
            }

            table[j][j + i] = ret;
        }
    }

    return table[0][values.size() - 1];
}