LeetCode(找出美丽数组的最小和)

135 阅读3分钟

题目内容

给你两个正整数:n 和 target 。

如果数组 nums 满足下述条件,则称其为 美丽数组 。

  • nums.length == n.
  • nums 由两两互不相同的正整数组成。
  • 在范围 [0, n-1] 内,不存在 两个 不同 下标 i 和 j ,使得 nums[i] + nums[j] == target 。

返回符合条件的美丽数组所可能具备的 最小 和,并对结果进行取模 109 + 7

示例 1:

输入:n = 2, target = 3
输出:4
解释:nums = [1,3] 是美丽数组。
- nums 的长度为 n = 2 。
- nums 由两两互不相同的正整数组成。
- 不存在两个不同下标 i 和 j ,使得 nums[i] + nums[j] == 3 。
可以证明 4 是符合条件的美丽数组所可能具备的最小和。

示例 2:

输入:n = 3, target = 3
输出:8
解释:
nums = [1,3,4] 是美丽数组。
- nums 的长度为 n = 3 。
- nums 由两两互不相同的正整数组成。
- 不存在两个不同下标 i 和 j ,使得 nums[i] + nums[j] == 3 。
可以证明 8 是符合条件的美丽数组所可能具备的最小和。

示例 3:

输入:n = 1, target = 1
输出:1
解释:nums = [1] 是美丽数组。

提示:

  • 1 <= n <= 109
  • 1 <= target <= 109

题目分析

题目要求可能的最小值,第一时间想到的就是贪心算法。 贪心算法是一种在每一步选择中都采取在当前状态下最好或最优的选择,从而希望导致结果是最好或最优的算法。

但是,仔细审题会发现,题目要求美丽数组内两两不同的数加起来不能等于目标值! 因此,我们需要对贪心策略进行一些调整。

我们可以将范围锁定在完美数组内小于目标值,等于目标值或者大于目标值的数上。

  • 小于目标值的数 就是1到2/target的值。这是因为,在范围内如果在两个数x和y,使得x + y < target,并且x + y ≠ targrt,那么我们可以将x和y替换为1和2/target,这样既可以满足美丽数组的性质并且保持最优解。
  • 等于目标的数 直接添加。
  • 大于目标值的数 按照贪心算法,它们的值就是target + 1,target + 2…… 这是因为,如果完美数组中存在两个数x和y,使得x + y > target,那么我们可以将x和y替换为target + 1和target + 2,这样可以使目标值增大,而不会改变美丽数组的性质。

画个图表示

下图取4为target值那么在1到target / 2(也就是2)的完美数组成员两两相加是不可能到达target的,等于taget值加上任意值都不会等于target,而大于target使用target + 1更不可能相加等于taget它们只会完全大于target。

image.png

代码编程

此代码通过测试用例,可提交后超时!

class Solution(object):
    def minimumPossibleSum(self, n, target):
        """
        :type n: int
        :type target: int
        :rtype: int
        """
        mod = 10 ** 9 + 7
        res = 0
        m = target // 2
        count = 0
        current = 1
        add_num = 1
        while count != n:
            if current <= m:
                res += current
                count += 1
            elif current == target:
                res += target
                count += 1
            elif current >= target:
                res += target + add_num
                count += 1
                add_num += 1
            current += 1
        return res % mod

image.png

代码优化(查看答案后)

这是来自leetcode官方题解。 原来的思路是正确的,但是面对大数时会超时。为了解决这个问题,我们可以使用数学的方式利用等差数列求和公式对代码进行优化。

等差数列求和公式为:

Sn = n/2 * (a1 + an)

其中,Sn是等差数列的前n项和,a1是第一项,an是第n项。

我们可以将完美数组看作一个等差数列,其中公差为1。 因此,我们可以使用等差数列求和公式来计算完美数组中所有小于或等于目标值的数的和。

image.png

代码展示

class Solution(object):
    def minimumPossibleSum(self, n, target):
        """
        :type n: int
        :type target: int
        :rtype: int
        """
        mod = 10 ** 9 + 7
        m = target // 2
        if n <= m:
            return ((1 + n) * n // 2) % mod
        return ((1 + m) * m // 2 + (target * 2 + (n - m) - 1) * (n - m) // 2) % mod