2834. 找出美丽数组的最小和

题目描述:

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

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

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

返回符合条件的美丽数组所可能具备的 最小 和,并对结果进行取模 10^9 + 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 <= 10^9
  • 1 <= target <= 10^9

解题分析及思路:

方法一:哈希表

思路:

为使得数组的和最小,我们可以从1开始,依次找到满足条件的数,直到找到n个数为止。

而条件为:在已经添加过的数中,不存在一个数,使得两个数的和等于target,即target-x不在已经添加的数中。

那么就变成如何判断target-x是否在已经添加的数中,这里可以使用哈希表来存储已经添加的数。

func minimumPossibleSum(n int, target int) int {
	const mod = 1e9 + 7
	m := make(map[int]bool)
	count := 0
	x := 1
	for count < n {
		if !m[target-x] {
			m[x] = true
			count++
		}
		x++
	}
	var sum int
	for key := range m {
		sum += key
	}
	return sum % mod
}

可惜的是,这种方法在n很大的时候,会超时。

复杂度:

  • 时间复杂度:O(N),其中 N 是x的大小。
  • 空间复杂度:O(N),只使用常数额外空间。

方法二:数学

思路:

由于方法一超时,我们需要寻找更优的方法。

那么是否可以通过数学的方法来解决呢?

我们可以发现:

  • 当从1开始添加数时,直到添加到target/2为止,而(target/2, target)之间的数,不能添加,因为这些数与[1, target/2]之间的数相加,必定会等于target。
  • 若添加到target/2后,所有个数仍小于n时,就要从target 开始添加,直到添加到n个数为止。

那么此时就是求[1, target/2]之间的数的和,以及[target, target+n-target/2]之间的数的和。

m=target/2所以能够得到如下的公式:

sum = (1+2+...+target/2) + (target + target+1 + ... + (target+n-target/2))
=> sum = m*(m+1)/2 + (target+target+n-m)*(target+n-m)/2 - (target-1)*(target)/2
=> sum = (m*(m+1) + (target+n-m-1)*(target+n-m) - (target-1)*(target))/2
=> sum = (m*m + m + target*n - target*m - n*m + (n*n-n)/2)

最后对结果取模即可,其中存在两种情况:

  • 当n<=m时,直接返回(1+2+…+n)的和。
  • 当n>m时,返回上述公式的结果。
func minimumPossibleSum2(n int, target int) int {
	const mod int = 1e9 + 7
	m := target / 2
	if n <= m {
		return ((1 + n) * n / 2) % mod
	}
	return (m*m + m + target*n - target*m - n*m + (n*n-n)/2) % mod
}

复杂度:

  • 时间复杂度:O(1)
  • 空间复杂度:O(1)

执行结果:

  • 执行耗时:0 ms,击败了100.00% 的Go用
  • 内存消耗:2 MB,击败了18.18% 的Go用户

通过次数 24.5K 提交次数 68.4K 通过率 35.7%

Related Posts

1026. 节点与其祖先之间的最大差值

## 题目描述:给定二叉树的根节点 root,找出存在于 不同 节点 A 和 B 之间的最大值 V,其中 V = |A.val - B.val|,且 A 是 B 的祖先。(如果 A 的任何子节点之一为 B,或者 A 的任何子节点是 B 的祖先,那么我们认为 A 是 B 的祖先)示例 1: ![](/img/leetcode/1026节点与其祖先之间的最大差值/tmp-tre

read more

1038. 从二叉搜索树到更大和树

## 题目描述:给定一个二叉搜索树 root (BST),请将它的每个节点的值替换成树中大于或者等于该节点值的所有节点值之和。提醒一下, 二叉搜索树 满足下列约束条件:- 节点的左子树仅包含键 小于 节点键的节点。 - 节点的右子树仅包含键 大于 节点键的节点。 - 左右子树也必须是二叉搜索树。*示例 1:***![](/img/leetcode/

read more

105. 从前序与中序遍历序列构造二叉树

## 题目描述:给定两个整数数组 preorder 和 inorder ,其中 preorder 是二叉树的先序遍历, inorder 是同一棵树的中序遍历,请构造二叉树并返回其根节点。示例 1: ``` 输入: preorder = [3,9,20,15,7], inorder = [

read more

106. 从中序与后序遍历序列构造二叉树

## 题目描述:给定两个整数数组 inorder 和 postorder ,其中 inorder 是二叉树的中序遍历, postorder 是同一棵树的后序遍历,请你构造并返回这颗 二叉树 。示例 1: ``` 输入:inorder = [9,3,15,20,7], postorder

read more

109. 有序链表转换二叉搜索树

## 题目描述:给定一个单链表的头节点 head ,其中的元素 按升序排序 ,将其转换为高度平衡的二叉搜索树。本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差不超过 1。示例 1: ``` 输入: head = [-10,-3,0,5,9] 输出: [0,-3,9

read more

114. 二叉树展开为链表

## 题目描述:给你二叉树的根结点 root ,请你将它展开为一个单链表:- 展开后的单链表应该同样使用 TreeNode ,其中 right 子指针指向链表中下一个结点,而左子指针始终为 null 。 - 展开后的单链表应该与二叉树 先序遍历 顺序相同。示例 1: ``` 输入:root

read more