【问题标题】:Time complexity on iterative and recursive solution迭代和递归解决方案的时间复杂度
【发布时间】:2017-01-25 20:08:15
【问题描述】:

我正在尝试解决以下问题:

我觉得我已经考虑了很多想法并尝试了很多东西。我设法解决了它,并产生了正确的值,但问题是它的时间效率不够高。它完成了 Kattis 测试中的 2 次,但由于超过了 1 秒的时间限制,第 3 次失败。恐怕我无法看到他们测试的输入是什么。

我从递归解决方案开始并完成了它。但后来我意识到它的时间效率不够高,所以我尝试改用迭代解决方案。

我从读取输入开始并将其添加到 ArrayList。然后我调用以下方法,目标为 1000。

public static int getCorrectWeight(List<Integer> platesArr, int target) {
    /* Creates two lists, one for storing completed values after each iteration,
    one for storing new values during iteration. */
    List<Integer> vals = new ArrayList<>();
    List<Integer> newVals = new ArrayList<>();

    // Inserts 0 as a first value so that we can start the first iteration.
    int best = 0;
    vals.add(best);

    for(int i=0; i < platesArr.size(); i++) {
        for(int j=0; j < vals.size(); j++) {
            int newVal = vals.get(j) + platesArr.get(i);
            if (newVal <= target) {
                newVals.add(newVal);
                if (newVal > best) {
                    best = newVal;
                }
            } else if ((Math.abs(target-newVal) < Math.abs(target-best)) || (Math.abs(target-newVal) == Math.abs(target-best) && newVal > best)) {
                best = newVal;
            }
        }
        vals.addAll(newVals);
    }
    return best;
}

我的问题是,对于大量数据,有什么方法可以降低这个时间复杂度?

【问题讨论】:

  • 你应该使用动态规划
  • 从背包问题或基于子集和问题的算法(动态编程、分支定界;可能结合启发式算法)中获得灵感。这个问题很可能是 np-hard 问题,这意味着在给定通用算法的情况下存在一些非常困难(不可能解决)的实例。如果没有对实例的一些假设,就很难(根据经验)指导您进行禁食。
  • 迭代与递归......这可以给你几个百分比的加速,在一些罕见的情况下可能更多......不值得做。更好的算法可能会将指数时间更改为例如二次 - 无限的胜利。

标签: java algorithm optimization iteration time-complexity


【解决方案1】:

主要问题是valsnewVals 的大小会增长得非常快,因为每次迭代都会使它们的大小翻倍。您只需要存储 1000 个左右的值,这些值应该是可管理的。您正在限制这些值,但因为它们存储在 ArrayList 中,所以最终会产生很多重复值。

如果改为使用HashSet,那么它应该对效率有很大帮助。

【讨论】:

    【解决方案2】:

    只需要存储一个大小为 2001(0 到 2000)的 DP 表 让dp[i] 表示是否可以形成ikg 的重量。如果权重超出数组边界,请忽略它。 例如:

    dp[0] = 1;      
    for (int i = 0; i < values.size();  i++){
        for (int j = 2000; j >= values[i]; j--){  
            dp[j] = max(dp[j],dp[j-values[i]);
        }
    }
    

    这里,values 是所有原始权重的存储位置。 dp 的所有值都将设置为 0,dp[0] 除外。

    然后,检查 1000 是否可以成功。如果没有,请检查 999 和 1001 等。 这应该在O(1000n + 2000) 时间内运行,因为n 最多为1000 这应该及时运行。

    顺便说一下,这是一个修改过的背包算法,您可能想查找一些其他变体。

    【讨论】:

      【解决方案3】:

      如果你对这类问题的思考过于笼统,你可能会认为你必须检查所有可能的输入组合(每个权重都可以包含或排除),给你 2n 个组合来测试如果你有 n 个输入。然而,这并不重要。相反,这里的关键是所有权重都是整数,目标是 1000。

      让我们先检查极端情况,因为这限制了搜索空间。

      如果所有权重 >= 1000,则选择最小的。

      如果至少有一个权重 = 2000 的权重,因此您可以忽略任何 >= 1000 的权重进行组合。

      然后,应用动态规划。保留前 k 个输入的所有组合的集合(你得到了 HashSet 作为其他海报的建议,但 BitSet 更好,因为其中的最大值非常小),并通过将所有先前的解决方案与 k+1 组合来增加 k '第一个输入。

      当您考虑了所有可能性后,只需搜索位向量以获得最佳响应。

      static int count() {
          int[] weights = new int[]{900, 500, 498, 4};
      
          // Check for corner case to limit search later
          int min = Integer.MAX_VALUE;
          for (int weight : weights) min = Math.min(min, weight);
          if (min >= 1000) {
              return min;
          }        
          // Get all interesting combinations
          BitSet combos = new BitSet();
          for (int weight : weights) {  
              if (weight < 1000) {
                  for (int t = combos.previousSetBit(2000 - weight) ; t >= 0; t = combos.previousSetBit(t-1)) {
                      combos.set(weight + t);
                  }
                  combos.set(weight);
              }
          }
          // Pick best combo
          for (int distance = 0; distance <= 1000; distance++) {
              if (combos.get(1000 + distance)) {
                  return 1000 + distance;
              }
              if (combos.get(1000 - distance)) {
                  return 1000 - distance;
              }
          }
          return 0;
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-07-22
        • 1970-01-01
        • 1970-01-01
        • 2020-11-13
        • 2023-03-24
        • 2023-03-04
        • 1970-01-01
        • 2015-08-10
        相关资源
        最近更新 更多