LeetCode第166场周赛(Weekly Contest 166)解题报告

第一题:简单题,按题意模拟即可。

第二题:遍历枚举。

第三题:二分。

第四题:二进制枚举所有变动情况,找出最小变动次数。

详细题解如下。


1. 整数的各位积和之差(Subtract the Product and Sum of Digits of An Integer)

           AC代码(C++)

2. 用户分组(Group the People Given the Group Size They Belong to)

           AC代码(C++)

3.使结果不超过阈值的最小除数(Find the Smallest Divisor Given A Threshold)

           AC代码( C++)

4.转化为全零矩阵的最少反转次数(Minimum Number of Flips to Convert Binary Matrix to Zero Matrix)

           AC代码( C++)


LeetCode第166场周赛地址:

https://leetcode-cn.com/contest/weekly-contest-166/


1. 整数的各位积和之差(Subtract the Product and Sum of Digits of An Integer)

题目链接

https://leetcode-cn.com/problems/subtract-the-product-and-sum-of-digits-of-an-integer/

题意

给你一个整数 n,请你帮忙计算并返回该整数「各位数字之积」与「各位数字之和」的差。

示例 1:

输入:n = 234
输出:15 
解释:
各位数之积 = 2 * 3 * 4 = 24 
各位数之和 = 2 + 3 + 4 = 9 
结果 = 24 - 9 = 15

示例 2:

输入:n = 4421
输出:21
解释: 
各位数之积 = 4 * 4 * 2 * 1 = 32 
各位数之和 = 4 + 4 + 2 + 1 = 11 
结果 = 32 - 11 = 21

提示:

  • 1 <= n <= 10^5

解题思路

根据题目意思,只要我们分别求出「各位数字之积」与「各位数字之和」即可。

简单的模拟题,主要就是得到一个数字的各个位上的数。然后累乘和累加即可。

AC代码(C++)

class Solution {
    public int subtractProductAndSum(int n) {
        int ans1 = 1;
        int ans2 = 0;
        while(n != 0)
        {
            int temp = n %10;
            n /= 10;
            ans1 *= temp;
            ans2 += temp;
        }
        return (ans1-ans2);
    }
}

2. 用户分组(Group the People Given the Group Size They Belong to)

题目链接

https://leetcode-cn.com/problems/group-the-people-given-the-group-size-they-belong-to/

题意

有 n 位用户参加活动,他们的 ID 从 0 到 n - 1,每位用户都 恰好 属于某一用户组。给你一个长度为 n 的数组 groupSizes,其中包含每位用户所处的用户组的大小,请你返回用户分组情况(存在的用户组以及每个组中用户的 ID)。

你可以任何顺序返回解决方案,ID 的顺序也不受限制。此外,题目给出的数据保证至少存在一种解决方案。

示例 1:

输入:groupSizes = [3,3,3,3,3,1,3]
输出:[[5],[0,1,2],[3,4,6]]
解释: 
其他可能的解决方案有 [[2,1,6],[5],[0,4,3]] 和 [[5],[0,6,2],[4,3,1]]。

示例 2:

输入:groupSizes = [2,1,3,3,3,2]
输出:[[1],[0,5],[2,3,4]]

提示:

  • groupSizes.length == n
  • 1 <= n <= 500
  • 1 <= groupSizes[i] <= n

解题思路

题目的意思是,对于 0 到 n-1个ID的用户,根据groupSizes数组中的“数字分组”,对应的数字表示这个ID所属的用户组中对应有几个人。

1 <= groupSizes[i] <= n,根据groupSizes数组中的数字大小范围,我们可以枚举从 1 到 n(比如当前为 i ),然后遍历整个groupSizes数组有没有对应相等的。如果有的话,说明对于该数字的分组是存在的。从而找到 出现了 i 次的这个数,把这几个用户存为同一组。

复杂度是 O(N^2),不会超时

AC代码(C++)

class Solution {
public:
    vector<vector<int>> groupThePeople(vector<int>& groupSizes) {
        vector<vector<int>> ans;
        vector<int> rows;
        int n = groupSizes.size();
        for(int i = 1; i <= n;++i)
        {
            int cnt = i;  // 如果出现这个数,表示对应有 cnt 个用户在同一组
            for(int j = 0;j < n;++j)
            {
                
                if(groupSizes[j]  == i)  // 找到的这几个用户是分到同一组的
                {
                    rows.push_back(j);
                    groupSizes[j] = -1;  //  为了避免重复找到同一个用户,这个用户被找过,我们就置于 -1
                    --cnt;
                    if(cnt == 0)  // 当前分组已经找完了,可以存进二维数组中了
                    {
                        ans.push_back(rows);
                        rows.clear();
                        --i;   // 这里是为了对于同一种分组,可能出现多次,比如示例1 中的,3,就出现了 2 组。
                        break;
                    }
    
                }
            }
        }
        return ans;
    }
};

3.使结果不超过阈值的最小除数(Find the Smallest Divisor Given A Threshold)

题目链接

https://leetcode-cn.com/problems/find-the-smallest-divisor-given-a-threshold/

题意

给你一个整数数组 nums 和一个正整数 threshold  ,你需要选择一个正整数作为除数,然后将数组里每个数都除以它,并对除法结果求和。

请你找出能够使上述结果小于等于阈值 threshold 的除数中 最小 的那个。

每个数除以除数后都向上取整,比方说 7/3 = 3 , 10/2 = 5 。

题目保证一定有解。

示例 1:

输入:nums = [1,2,5,9], threshold = 6
输出:5
解释:如果除数为 1 ,我们可以得到和为 17 (1+2+5+9)。
如果除数为 4 ,我们可以得到和为 7 (1+1+2+3) 。如果除数为 5 ,和为 5 (1+1+1+2)。

示例 2:

输入:nums = [2,3,5,7,11], threshold = 11
输出:3

示例 3:

输入:nums = [19], threshold = 5
输出:4

提示:

  • 1 <= nums.length <= 5 * 10^4
  • 1 <= nums[i] <= 10^6
  • nums.length <= threshold <= 10^6

 

解题分析

拿到这道题目,就是找到一个除数,使得满足 nums 数组中的每个数的进行除法的向上取整结果之和 小于等于给定的阈值 threshold。

一开始的方法是根据暴力枚举,也就是枚举所有可能的除数,接着求出向上取整结果之和,判断是否满足要求。枚举的上界是max(nums[i]),因为当是最大值的时候,整个nums数组的除法结果都是1,所以和就是 N = nums.length ,刚好满足nums.length <= threshold 。那么时间复杂度是 O(max(nums[i])  * N ),那么根据数据大小,应该是超时的。

那么接着考虑要找到可能的除法,我们也能知道整个除法的范围(1 到 1e6 ),如果要降低复杂度就是对可能答案的查找不能用枚举。那么可能的就是二分查找。

由于二分查找的复杂度是 O(log (max(nums[i]))),这样子时间复杂度就够。

判断是否能够二分查找,就是判断能否缩小范围。

比如对于示例1,假设已经缩小了范围到 1 到 9

输入:nums = [1,2,5,9], threshold = 6

第一次范围 1-9,中间值为5,判断了 5 可以,此时要找最小的
第二次范围 1-5,中间值为3,判断了 3 不可以,所以范围要变大
第三次范围 4-5,中间值为4,判断了 4 不可以
第四次范围 5-5,跳出
所以值为 5

所以在枚举的情况下,改成二分查找除数范围,从而时间复杂度不会超时。

AC代码(C++)

class Solution {
public:
    int smallestDivisor(vector<int>& nums, int threshold) {
        int l = 1, r = 1e6;  // 一开始的范围
        while(l < r)  // 二分
        {
            int mid = (l+r)/2;
            int sum = 0;
            for(auto num : nums)
            {
                if(num % mid == 0)
                    sum += num/mid;
                else
                    sum += num/mid + 1;
            }
            if(sum <= threshold) r = mid;  // 判断满足,那就是范围在左边
            else l = mid+1;  // 不满足条件,那就是范围在右边
        }
        return l;
    }
};

4.转化为全零矩阵的最少反转次数(Minimum Number of Flips to Convert Binary Matrix to Zero Matrix)

题目链接

https://leetcode-cn.com/problems/minimum-number-of-flips-to-convert-binary-matrix-to-zero-matrix/

题意

给你一个 m x n 的二进制矩阵 mat。

每一步,你可以选择一个单元格并将它反转(反转表示 0 变 1 ,1 变 0 )。如果存在和它相邻的单元格,那么这些相邻的单元格也会被反转。(注:相邻的两个单元格共享同一条边。)

请你返回将矩阵 mat 转化为全零矩阵的最少反转次数,如果无法转化为全零矩阵,请返回 -1 。

二进制矩阵的每一个格子要么是 0 要么是 1 。

全零矩阵是所有格子都为 0 的矩阵。

示例 1:

输入:mat = [[0,0],[0,1]]
输出:3
解释:一个可能的解是反转 (1, 0),然后 (0, 1) ,最后是 (1, 1) 。

示例 2:

输入:mat = [[0]]
输出:0
解释:给出的矩阵是全零矩阵,所以你不需要改变它。

示例 3:

输入:mat = [[1,1,1],[1,0,1],[0,0,0]]
输出:6

示例 4:

输入:mat = [[1,0,0],[1,0,0]]
输出:-1
解释:该矩阵无法转变成全零矩阵

提示:

  • m == mat.length
  • n == mat[0].length
  • 1 <= m <= 3
  • 1 <= n <= 3
  • mat[i][j] 是 0 或 1 。

解题分析

对于矩阵中的一个位置而言,要么变动,要么不变动。变动两次或者偶数次相当于没变,变动奇数次和变动一次效果是一样的。

根据数据范围可以知道,一个矩阵最多是 3*3 的大小,那么也就是相当于有9个位置,每个位置有两种情况(变或者不变)。

那么我们可以枚举所有情况,也就是2^9种可能,然后依次判断对于每一种可能的时候。该可能能否变为全0,如果能,那就取能变为全0的所有情况种,该数字对应二进制种的 1 的个数(表示该位置变动了)。

我们可以一开始设变动次数为 100(因为最多的变动次数只可能是9),如果最后所有情况都遍历完了,变动次数还是100(即大于9以上的话,那就是表示之前没有一种情况可以变为全0,不然变动次数只可能 <= 9)。

因此这道题最主要就是枚举所有情况,总共 2^(n*m) 种,然后判断每一种中,只要二进制对应为 1 就表示要变动,最后判断这种可能是不是全0。

AC代码( C++)

class Solution {
public:
    int minFlips(vector<vector<int>>& mat) {
        // 数据很小,对于每一个位置,要么变动,要么不变动,变动两次相当于没变动
        // 所以可以枚举所有可能,最多9个格,2^9种变动的可能,然后判断哪种可能可以得到最后答案,并且变动(1)最少的
        int dx[4] = {1, 0, -1, 0};
        int dy[4] = {0, 1, 0, -1};
        
        int ans = 100;  // 记录最小的 1 个数,所以设最大值(整个矩阵变动最多是 n*m,所以只要 > 10 即可)
        int n = mat.size(), m = mat[0].size();
        int lim = 1<<(n*m);  // 总的枚举可能
        
        for(int i = 0;i < lim;i++)
        {
            vector<vector<int>> g = mat;  // 每一种情况都要改变,所以备份原来的矩阵进行改变
            int cnt = 0;
            for(int j = 0;j < n*m;++j)  // 找出这种可能中,有几个 1 
            {
                if(i & (1<<j))  // 如果该位置是 1,所以要变动反转
                {
                    cnt++;
                    int x = j/m, y = j%m;  // 从一个一维变到二维对应的左边
                    g[x][y] = 1-g[x][y];  // 本身的位置变动
                    for(int ii = 0;ii < 4;ii++)  // 相邻的四个位置
                    {
                        int xx = x + dx[ii], yy = y + dy[ii];
                        if(xx<0 || xx>=n || yy<0 || yy>=m) continue;  // 如果出到边界外,就不存在这个位置要进行变动
                        g[xx][yy] = 1-g[xx][yy];
                    }
                }
            }
            
            int flag = 1;  // 最后判断是不是为 全 0
            for(int ii = 0;ii < n;ii++)
                for(int jj = 0;jj < m;jj++)
                    if(g[ii][jj] == 1) flag = 0;
            
            if(flag) ans = min(ans, cnt);  // 如果是全 0 ,那就取此时 1 个数和原本的 ans取最小值
            
        }
        if(ans > 10) return -1; // 表示没有一种可能可以变为全0,不然ans是 <= n*m <= 9
        return ans;
        
        
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值