【问题标题】:How many breaks does it take to divide a chocolate completely?完全分割一块巧克力需要多少次休息?
【发布时间】:2019-07-01 11:06:14
【问题描述】:

CodeWars 再次挑战。今天我遇到了这个问题:

“你的任务是将给定尺寸 n x m 的巧克力棒分成小方块。每个方块的大小为 1x1 且牢不可破。实现一个函数,该函数将返回所需的最小中断数。

例如,如果给您一块 2 x 1 大小的巧克力棒,您可以一次将其拆分为单个正方形,但对于 3 x 1 大小,您必须拆分两次。

如果输入数据无效,则应返回 0(因为如果我们没有要拆分的巧克力,则不需要中断)。输入将始终为非负整数。”

由于某种原因,无论我提供巧克力棒的哪一边,输出始终为 0。

我已经尝试过的:

object breakChocolate {

    var result = 0

    def breakChocolate(n: Int, m: Int) = {

        var t = n*m
        var i =0
        def breaking(y:Int): Unit ={
            if (t ==0 || t ==1)
                result = i
            else {
                breaking(t%2)
                i +=1
            }
        }
        result
    }
}

这里是测试:

测试结果: 测试用例 breakChocolate(5, 5) 应该返回 24 测试失败

0 不等于 24 堆栈跟踪 38ms 完成 breakChocolate(7, 4) 应该返回 27 测试失败

0 不等于 27 堆栈跟踪 1ms内完成 76ms 完成

【问题讨论】:

  • 我不会对算法本身发表评论,但是您的代码将始终返回 0 的原因非常简单:您定义了一个修改 result 的方法 breaking,但你从来没有真正调用它。
  • 有趣的问题。开始用笔和纸涂鸦,并注意到尺寸和所需切割数量之间的相关性。任何2x2 需要3 次切割,任何2x3 需要5 次切割,任何3x3 需要8 次切割,任何3x4 需要11 次切割......注意到一个模式了吗?
  • 是的,这意味着切割的数量总是比所有碎片的总数小一倍。啊,好的,现在就知道了。我只是在想一个数学算法的衍生物,用数学语言来解释整个事情,但是是的,我确实明白了一般的想法:)
  • 顺便说一句,抱歉回复晚了,正在度假:)

标签: scala recursion int


【解决方案1】:

要解决这个问题,您根本不需要递归。考虑巧克力盘的特殊情况:(1 x n)。要完全划分这个盘子,你需要 (n-1) 次休息。现在你有板m x n。要将其分成 m 份表格 (1 x n),您需要 (m-1) 个休息点。所以总的休息次数是

(m-1) + m*(n-1) ~ 
m - 1 + m*n - m ~ 
m*n - 1

【讨论】:

  • 干杯,这似乎完全回答了我的问题。
  • 只有一个问题。在第一行,为什么要加 (m-1)+ m*(n-1)?
  • 我们正在打碎一块大小为 m*n 的巧克力。我知道 n-1 来自哪里,但是为什么要添加 (m-1) 。是不是因为我们需要包含 n 位和 m 位的中断?
  • 如果 m 和 n 是非常高的值,比如每个 10^9 怎么办?
  • 没关系。如果你的意思是数字溢出,那么你可以使用一些可以处理数字的类型,比如 10^20
【解决方案2】:

如果我正确阅读了 Scala,那么你的基本算法就错了。

这实际上是一个非常简单的问题,类似于旧的难题:如果您有 55 支球队参加单淘汰赛,显然其中一些球队必须在第一轮轮空,所以不会有一个完美的均匀支架。那么总共会玩多少场比赛呢?

答案:54。无论支架是如何制作的,它都是单淘汰赛。每场比赛都会将剩余球队的数量减少一个。因此,要让 55 名参与者减少到一名获胜者,必须进行 54 场比赛。

您的巧克力棒也有类似的说法。在某些时候,你面前有p 块巧克力。无论你选择打破哪一个,你已经从堆中取出1 并放回2,这意味着堆现在有p + 1 块。因此,对于每个拆分,您都会在堆中添加一个。这应该直接导致答案......

...这实际上可能是错误的,因为在某些情况下需要返回 0,但应该很容易实现特殊情况。

【讨论】:

  • 您帖子中的有趣问题和非常相关的要点。我在视觉上完成了它,但最终得出了相同的答案。我特别喜欢你的p + 1 位,这确实巩固了我的推理。感谢分享,斯科特。
【解决方案3】:

你得到 0 因为你没有运行breaking

如果您想使用递归,一种选择是使用尾递归函数。

首先递减a 检查它是否大于 1 以获得“水平”中断的数量以获得切片。循环时将累加器加 1。

然后递减b 检查它是否大于 1 以获得“垂直”中断的数量。这次添加起始“水平”值,因为这是您实际必须打破切片的次数。

object breakChocolate {    
  def breakChocolate(n: Int, m: Int): Int = {
    def breaking(a: Int, b: Int, acc: Int = 0): Int = {
      if (a > 1) breaking(a - 1, b, acc + 1)
      else if (b > 1) breaking(a, b - 1, acc + n)
      else acc
    }
    breaking(n, m)
  }
}

Scala demo

【讨论】:

  • 正确,但对于 n * m - 1 而言,这仍然需要大量工作。
  • @jwvh 没错 :) 这只是一个示例,说明您如何以递归方式对其进行推理。
  • 令我惊讶的是,这个问题被简化为简单的算术。虽然很高兴看到递归解决方案,但没有任何暗示或建议表明此处介绍的算法会达到 最佳 切割数。碰巧的是,对于这个特定问题,无论您在哪里切割,相同的输入巧克力都需要相同数量的切割。这次你很幸运:D
  • @user633183 感谢您的评论。 OP 尝试使用递归解决方案,这就是我添加可能解决方案的原因。我解释了我是怎么想的,不过我不认为这是运气:-)
【解决方案4】:

您可以改用此代码:

  function breakChocolate(n,m) {
   if(n > 0 && m > 0) {
      return n * m - 1;
  } else {
    return 0;
  }
  }

【讨论】:

  • 这不是和接受的答案一样吗? (顺便说一句,这是 Scala。不需要return。)
  • 是的,但是我们还要测试输入数据是否有效。如果有任何巧克力要拆分,则返回数字。但如果数据无效,例如:- (-1) 或 0,它将返回 0。
  • 我认为否定结果将是无效输入的一个很好的指标,但是,好的,如果您觉得使用零作为标准指标很重要,那么为什么在发布的答案中没有提到呢?在 SO 上不鼓励仅使用代码的答案。此外,这是一个 Scala 问题,但您答案中的代码不是 Scala。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-03-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多