前端算法第一九三弹-检查「好数组」

123 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第27天,点击查看活动详情

给你一个正整数数组 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, 61029*1 + 6*(-3) + 10*(-1) = 1

示例 3:

输入:nums = [3,6]
输出:false

关于裴蜀定理,大家可以参考维基百科

简单来说,对于a1,a2,a3,... an 的 n 个数, 一定存在 a1 * n1 + a2 * n2 + a3 * n3 + ... + an * nn = d 。 其中 n1,n2,n3...nn 是正整数, d 为 a1,a2,a3,... an 的 n 个数的最大公约数。

而力扣不止一道题用到了这个定理,比如 365. 水壶问题

因此自然而然, 这道题就是 d 为 1 的裴蜀定理问题, 因此我们的算法就是 check 存不存在一个子数组,其最大公约数为 1。而这个复杂度仍然很高, 我们仍然需要枚举所有的子数组。

实际上我们不需要, 因为如果数组 A 是数组 B 的子集的话,那么 A 的最大公约数是 1, B一定也是 1. 因此我们只需要求出整体的最大公约数即可。

根据欧几里德算法的定理:两个整数的最大公约数等于其中较小的那个数和两数相除余数的最大公约数,我们可以通过递归,不断缩小两个数字,直到某个数字为 0,则另外一个数字为最大公约数。

class Solution {
    public boolean isGoodArray(int[] nums) {
        
        int gcd = nums[0];
        if(gcd==1) return true;
        for(int i=1;i<nums.length;i++){
            gcd = Gcd(gcd,nums[i]);
            if(gcd == 1) return true;
        }
        return false;
    }
    public static int Gcd(int a,int b){
        return a%b==0?b:Gcd(b,a%b);
    }
}