来源:力扣(LeetCode)
描述:
给你一个正整数数组 nums
,你需要从中任选一些子集,然后将子集中每一个数乘以一个 任意整数,并求出他们的和。
假如该和结果为 1
,那么原数组就是一个「好数组」,则返回 True;否则请返回 False。
示例 1:
输入:nums = [12,5,7,23]
输出:true
解释:挑选数字 5 和 7。
5*3 + 7*(-2) = 1
示例 2:
输入:nums = [29,6,10]
输出:true
解释:挑选数字 29, 6 和 10。
29*1 + 6*(-3) + 10*(-1) = 1
示例 3:
输入:nums = [3,6]
输出:false
提示:
- 1 <= nums.length <= 105
- 1 <= nums[i] <= 109
前言
方法:数论
思路与算法
本题解涉及到数论中的「裴蜀定理」,题目给出一个正整数数组 nums,现在我们需要从中任选一些子集,然后将子集中的每一个数都乘以一个任意整数并求出他们的和,如果该和的结果为 1,那么原数组就是一个「好数组」。现在我们需要判断数组 nums 是否是一个「好数组」。由「裴蜀定理」可得,题目等价于求 nums 中的全部数字的最大公约数是否等于 1,若等于 1 则原数组为「好数组」,否则不是。
求 nums 中全部数字的最大公约数的方法为,我们设初始为 x = nums[0],然后对于每一个数 nums[i],0 < i < n,我们更新 x = gcd(x, nums[i])。遍历完全部数字后,x 即为数组 nums 中全部的元素的最大公约数。然后判断其是否等于 1 即可。在实现过程中我们也可以进一步做优化:如果遍历过程中出现最大公约数等于 1 的情况,则由于 1 和任何正整数的最大公约数都是 1,此时可以提前结束遍历。
代码:
class Solution {
public:
bool isGoodArray(vector<int>& nums) {
int divisor = nums[0];
for (int num : nums) {
divisor = gcd(divisor, num);
if (divisor == 1) {
break;
}
}
return divisor == 1;
}
};
执行用时:40 ms, 在所有 C++ 提交中击败了70.90%的用户
内存消耗:28.4 MB, 在所有 C++ 提交中击败了82.09%的用户
复杂度分析
时间复杂度:O(n+logm),其中 n 为数组 nums 的长度,m 为数组 nums 中的最大数,其中求单次最大公约数的时间复杂度为 O(logm),由于在每次求两个数的最大公约数时其中一个数保持单调不增,所以求总的公约数的时间复杂度为 O(logm)。
空间复杂度:O(1)。仅使用常量空间。
author:LeetCode-Solution