[HackerRank] Non-Divisible Subset 问题

题目地址:https://www.hackerrank.com/ch...

题目大意:

给定:

  1. 集合 S,其中含有不同的整数;
  2. k。

判定所能得到的 S 的子集 S' 的最大元素个数。其中,S' 需要满足:其中的任意两数之和均不能被 k 整除。

例子:给定集合 {1, 7, 2, 4} 和数字 3。我们可以将 2 从集合中删去,得到子集 {1, 7, 4},从而使得其中任意两数之和均不能被 3 整除。因而该情况的结果为 3。

编程语言:Scala

1. 参考思路

对于这一题目,如果我们采用递归方式求解,则会导致问题的复杂度极大,从而无法通过超时检测。我们需要另想思路。

1.1 转换 S 集合

首先,由于集合中的元素可能非常大。而在这一问题中,集合 S 与 k 的解,与 S(余) 与 k 的解是没有区别的。其中 S(余) 指的是对 S 中每一个元素求其对 k 的余数,所组成的集合。

对于 S(余),其中的每一个元素的值都小于 k。

在这个基础上,我们再考虑,对于 S(余) 中为 0 的元素,实际是不会对问题的结果产生影响的。因为 0 与任意数字求和结果相对 k 的整除性都不会因其而改变。也就是说,只要我们能保证,除 0 以外的其他元素的集合能够满足题目要求,则原集合也能满足。

现在,我们将 S 转换成了一个新集合 Sa,其中,Sa 为 S 中任一元素对 k 求余之后的集合,且其中不包含余数为 0 的数。

1.2 考虑集合中两数相加被 k 整除的情况

在集合 Sa 中,如果存在两个数字 m 和 n 能够被整除,那么 m + n 一定等于 k( 考虑 Sa 中元素的形式 )。

也就意味着,能够被整除,即不满足题目要求的数字是会成对出现的,二者仅会与对方形成不满足条件的组合,且和为 k。

那么对于这样一对数 m 和 n,我们需要从原集合中将 m 或 n 全部删除,才能保证子集满足题目所要求的条件。当然,为了达到题目要求的集合最大元素数,我们需要根据 m 和 n 在集合中的个数,删除较小的那个数。

这样一来,我们需要遍历 Sa 集合,得到其中每一个数字在集合中重复的个数。即得到一个 HashMap 对象,假设为 mapmap 的最大长度为 k - 1( 去除了 0 )。

然后,我们需要遍历 map,找到上述成对出现的数字组合,并记录每一对中最小个数的值。

但是,如果在遍历过程中,对每一个数字都进行处理的话,会导致重复计算。这里,我们需要约定,只计算数字组合中,较大的或较小的那个。由于 m + n 等于 k,所以我们可用 k / 2 作为判定条件。

但这又带来一个问题:k / 2 对 k 为奇数的情况是没有问题的。但当 k 为偶数时,由于值为 k / 2 的数字,其组合就是自身,那么我们需要对这一情况特殊考虑,即值为 k / 2 的数字在最终的子集中只能保留 1 个。

综上所述,通过对 S 集合的转换,和两次遍历,我们就可以得到最终的结果了。

但是,这里还有一个特例,即 k = 1 的情况。

1.3 k 为 1 的情况

当 k 为 1 时,由于任意数字都能被 1 整除,为了满足题目条件,我们只能将子集中元素的个数调整为 1,使得其无法满足「求和」。

2. 参考代码

由上述可知,参考代码如下,重点在于 S 集合的转换部分,以及 map 的生成和遍历:

def nonDivisibleSubset(k: Int, S: Array[Int]): Int = {
    var maxSubsetLength = 0
    // 注意 1
    if (1.equals(k)) {
        maxSubsetLength = 1
    } else {
        val hashMap = mutable.HashMap[Int, Int]()
        var invalidCount = 0
        for (item <- S) {
            val remainder = item % k
            if (0.equals(remainder) == false) {
                val current = hashMap.get(remainder)
                current match {
                    case None => hashMap(remainder) = 1
                    case Some(_) => hashMap(remainder) += 1
                }
            }else{
                invalidCount += 1
            }
        }
        maxSubsetLength = S.length - invalidCount
        if (invalidCount > 0) {
            maxSubsetLength += 1
        }
        var halfOfK = k / 2
        if (0.equals(k % 2)) {
            val countOfHalfK = hashMap.get(halfOfK)
            if (countOfHalfK != None) {
                maxSubsetLength = maxSubsetLength - countOfHalfK.get + 1
            }
            halfOfK = k / 2 - 1
        }

        for ((key, value) <- hashMap) {
            // 注意奇数偶数
            if (key <= halfOfK) {
                val complement = hashMap.get(k - key)
                if (complement != None) {
                    maxSubsetLength -= Math.min(value, complement.get)
                }
            }
        }
    }

    maxSubsetLength
}

代码 Gist 地址为:Answer of question from https://www.hackerrank.com/challenges/non-divisible-subset/problem · GitHub

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值