第一题:简单题,按题意模拟即可。
第二题:遍历枚举。
第三题:二分。
第四题:二进制枚举所有变动情况,找出最小变动次数。
详细题解如下。
1. 整数的各位积和之差(Subtract the Product and Sum of Digits of An Integer)
2. 用户分组(Group the People Given the Group Size They Belong to)
3.使结果不超过阈值的最小除数(Find the Smallest Divisor Given A Threshold)
4.转化为全零矩阵的最少反转次数(Minimum Number of Flips to Convert Binary Matrix to Zero Matrix)
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;
}
};