【问题标题】:Minimum steps to one logic fails for some conditions在某些情况下,一个逻辑的最小步骤失败
【发布时间】:2017-05-03 04:59:43
【问题描述】:

谜题是获得使数字为 1 所需的最少步数。允许的操作是

1. You can subtract 1 from the number
2. You can divide the number by 2 if it is divisible by 2.
3. You can divide the number by 3 if it is divisible by 3.

最后,您需要通过执行上述操作使数字为 1。我正在尝试获得一个解决方案,该解决方案为我提供了使数字为 1 所需的上述操作的最少数量。 我的代码(Java)如下。

public int minStepsBottomUp(int n) {
        int[] memoArray = new int[n+1];
        memoArray[0] = 0;
        memoArray[1] = 0;
        for(int i=2;i<=n;++i){
            int r = 1 + memoArray[i-1];
            if(n % 2 == 0) {
                r = Math.min(r, 1+memoArray[n/2]);
            }
            if(n % 3 == 0) {
                r = Math.min(r, 1+memoArray[n/3]);
            }
            memoArray[i] = r;
        }
        return memoArray[n];
    }

但我得到了一些模棱两可的结果。例如 - 如果数字是 5,我得到所需的最小步数为 4。实际上应该是 3。有人可以解释我哪里出错了吗?

【问题讨论】:

  • 如果它可以被 2 和 3 整除,例如 6,会发生什么?这些操作是否按优先顺序排列?
  • 您应该拿起笔和纸,手动操作,并将您的手动步骤与您的算法进行比较。
  • 3 与 5 的结果如何?
  • @Bruce Feldman 给出了正确的答案——您在原本出色的程序中出现了拼写错误。 n -> i 在各个地方。

标签: java algorithm dynamic-programming


【解决方案1】:

我建议反转问题:开始1我们应该到达n使用三种操作:

  • 加1
  • 乘以 2
  • 乘以 3

例如对于5,我们将有 3 个操作(乘以 3,加 1,加 1):

1 -> 3 -> 4 -> 5 

到目前为止一切顺利,现在我们有标准的动态规划问题; C# 实现:

private static int Best(int value) {
  if (value <= 0)
    return -1;  // or throw ArgumentOutOfRangeException
  else if (value == 1)
    return 0;

  Dictionary<int, int> best = new Dictionary<int, int>() { {1, 0} };

  List<int> agenda = new List<int>() { 1 };

  for (int step = 1; ; ++step) 
    for (int i = agenda.Count - 1; i >= 0; --i) {
      int item = agenda[i];

      agenda.RemoveAt(i);

      int[] next = new int[] { item + 1, item * 2, item * 3 };

      foreach (int v in next) {
        if (v == value)
          return step;

        if (!best.ContainsKey(v)) {
          best.Add(v, step);
          agenda.Add(v);
        }
      }
    }
}

测试:

// 3
Console.WriteLine(Best(5));
// 3
Console.WriteLine(Best(10));
// 7
Console.WriteLine(Best(100));
// 19
Console.WriteLine(Best(1000000));

【讨论】:

  • 除了原始问题中程序中的拼写错误之外,我不明白为什么这种方法更好。它并没有更快,而且似乎更复杂。
  • @Paul Hankin:对于“...*上述操作的最小数量*”,贪婪的方法(这是有问题的)不起作用;它返回(而且,是的,速度很快)一个很好的(过度)估计,但不是确切的数字。为了得到一个实际的最小值,我们必须实现动态编程或带记忆的递归。
  • 但原程序确实使用了动态规划。我的意思是,除了在一些地方用n 替换i 的拼写错误。
  • @Paul Hankin:我必须承认,您完全正确:在原始代码的循环中将n 更改为i,两种方法返回相同的结果(我已经测试了[1..1000] 范围)。在编写我的代码时,我只看到了 robin 的答案(贪婪的方法)。我为解决方案创建一个反例,陈述问题并记下我的版本。
【解决方案2】:

在循环中,您使用的是 n 而不是 i。

例如,n % 2 == 0 应该是 i % 2 == 0

【讨论】:

  • 没错,循环内的n -&gt; i替换解决了任务(代码更改最少)+1
【解决方案3】:

如果数字是 5,您可以通过以下方式获得 1:

int x = 5 - 1;
x = x - 1;
x= x / 3;

【讨论】:

    猜你喜欢
    • 2021-09-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-01
    • 2017-01-31
    • 1970-01-01
    • 2021-11-08
    • 1970-01-01
    相关资源
    最近更新 更多