[HackerRank] Birthday Chocolate 问题及其延伸

题目地址:Birthday Chocolate | HackerRank

题目大意:

给定:

  1. 数组 s
  2. 个数 m
  3. 总和 d

判定 s 中,连续 m 个数字总和为 d 的情况共有几种?

例子:给定数组 [1, 3, 2, 2, 7],判定该数组中,连续 2 个数字总和为 4 的情况共有几种?
答案:2 种,分别为 (1,3)(2,2)

编程语言:Scala

1. 原问题

由于原题中限定了连续,因而一个循环便可以得到答案:

def solve(s: Array[Int], d: Int, m: Int): Int = {
        val length = s.length
        var count = 0
        if(m > length) {
            count = 0
        } else {
            for ((_, index) <- s.zipWithIndex) {
                if (index + m - 1 <= length) {
                    val sum = s.slice(index, index + m).sum
                    if (sum.equals(d)) count += 1
                }
            }
        }
        count
    }

2. 延伸问题

2.1 问题介绍

如果我们把原题中的连续限制删去,问题就可以理解为:

给定一组数,每次选取 m 个数,可以选择的形式有哪些?

假定 m 小于等于数组的长度,下同。

至于这 m 个数的和是否等于 d,只需要将上述问题的结果依次求和判断即可。

如果只需要求出组合的个数,那就是排列组合问题。而若要得到组合的全部形式,则更类似于离散中的可读问题。

比如,对于数组 [1, 2, 3, 1],选择两个数字的所有形式为:(1, 2)(1, 3)(1, 1)(2, 3)(2, 1)(3, 1)

注意,对于数组 [1, 2, 3, 1],「第一位的 1 和第三位的 3」与「第三位的 3 和第四位的 1」,被看做是不同的组合,而「第一位的 1 和第三位的 3」与「第一位的 1 和第三位的 3」是相同的组合。即只考虑因颠倒顺序导致的重复。

2.2 参考思路

关于这一问题的求解和编码思路,可以参考:获取排列组合的结果集。

2.3 参考代码

根据 2.2 中的思路,我们可以编写递归方法如下:

/**
  * 递归函数
  *
  * @param count      可选个数
  * @param candidates 给定的待选数组
  * @param bag        前面递归层级中已经选择的 N,N',N''... 的元素集合
  * @param container  用于存储可选类型的容器
  */
def traverse(count: Int, candidates: ArrayBuffer[Int], bag: ArrayBuffer[Int], container: ArrayBuffer[ArrayBuffer[Int]]): Unit = {
    if (count <= candidates.length) {
        if (count.equals(1)) {
            for (item <- candidates) {
                val finalBag = bag.clone()
                finalBag += item
                container += finalBag
            }
        } else {
            for ((item, index) <- candidates.zipWithIndex) {
                val nextBag = bag.clone()
                nextBag += item
                val nextCandidates = candidates.slice(index + 1, candidates.length)
                traverse(count - 1, nextCandidates, nextBag, container)
            }
        }
    }
}

对于递归初始阶段,其待选数组即为问题中的全量数组 s。

在主函数中,我们可以这样调用该递归方法:

def main(args: Array[String]): Unit = {
    val candidates = ArrayBuffer[Int](1, 3, 7, 5, 9)
    val count = 3
    val container = ArrayBuffer[ArrayBuffer[Int]]()
    val bag = ArrayBuffer[Int]()

    traverse(count, candidates, bag, container)

    println(container.mkString("\n"))
}

其结果为:

ArrayBuffer(1, 3, 7)
ArrayBuffer(1, 3, 5)
ArrayBuffer(1, 3, 9)
ArrayBuffer(1, 7, 5)
ArrayBuffer(1, 7, 9)
ArrayBuffer(1, 5, 9)
ArrayBuffer(3, 7, 5)
ArrayBuffer(3, 7, 9)
ArrayBuffer(3, 5, 9)
ArrayBuffer(7, 5, 9)

代码 Gist 地址:Select all combinations of N elements from the collection. · GitHub

参考链接

  1. Birthday Chocolate | HackerRank
  2. How to convert a Scala Array to ArrayBuffer? - Stack Overflow
  3. What is the fastest way to sum a collection in Scala - Stack Overflow
  4. 获取排列组合的结果集 - DB.Reid - SegmentFault 思否

你可能感兴趣的:(算法)