342场周赛

前两题简单不记录了

第3题-滑动子数组的美丽值-6390

image-20230423145222059

示例 1:

1
2
3
4
5
6
输入:nums = [1,-1,-3,-2,3], k = 3, x = 2
输出:[-1,-2,-2]
解释:总共有 3 个 k = 3 的子数组。
第一个子数组是 [1, -1, -3] ,第二小的数是负数 -1 。
第二个子数组是 [-1, -3, -2] ,第二小的数是负数 -2 。
第三个子数组是 [-3, -2, 3] ,第二小的数是负数 -2 。

示例 2:

1
2
3
4
5
6
7
输入:nums = [-1,-2,-3,-4,-5], k = 2, x = 2
输出:[-1,-2,-3,-4]
解释:总共有 4 个 k = 2 的子数组。
[-1, -2] 中第二小的数是负数 -1 。
[-2, -3] 中第二小的数是负数 -2 。
[-3, -4] 中第二小的数是负数 -3 。
[-4, -5] 中第二小的数是负数 -4 。

示例 3:

1
2
3
4
5
6
7
8
输入:nums = [-3,1,2,-3,0,-3], k = 2, x = 1
输出:[-3,0,-3,-3,-3]
解释:总共有 5 个 k = 2 的子数组。
[-3, 1] 中最小的数是负数 -3 。
[1, 2] 中最小的数不是负数,所以美丽值为 0 。
[2, -3] 中最小的数是负数 -3 。
[-3, 0] 中最小的数是负数 -3 。
[0, -3] 中最小的数是负数 -3 。

提示:

1
2
3
4
5
n == nums.length 
1 <= n <= 105
1 <= k <= n
1 <= x <= k
-50 <= nums[i] <= 50

+++++++++++++++

思路:由于提示中给出的数据范围很小,-50到50,因此暴力解法就可以

定义一个数量数组,用来存储num中元素的数量,存储k个区间内元素的数量,再遍历数组求前x最小的值

代码为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class Solution {
public int[] getSubarrayBeauty(int[] nums, int k, int x) {
int n = nums.length;
int res[] = new int[n-k+1];
int cnt[] = new int[101];
//因为要记录每个元素的个数,因此将cnt中的下标为num的值,cnt的值为num元素的个数,由于,num中元素有负数,所以全部+50变为正数
for (int i = 0;i<n;i++){
nums[i]+=50;
}
//计算前k个区间元素的个数,保存到cnt数组中
for (int i = 0;i<k;i++){
cnt[nums[i]]++;
}
int index = 0;
res[index++] = findMin(cnt,x); //求第x个最小值
//依次求之后的k个区间的第x个最小值
for (int i = 1;i<n-k+1;i++){
cnt[nums[i-1]]--; //因为下一个区间的最小值向后移动了一位,也就是前面的那个数不在这个区间内了,因此数量要-1。
cnt[nums[i+k-1]]++; //下一个区间,加入一个数,该数的数量+1.
res[index++] = findMin(cnt,x);
}
return res;
}
//找到第x最小值
public int findMin(int cnt[],int x){
for (int i = 0;i<cnt.length;i++){
x = x-cnt[i];
if (x<=0) return i<50?i-50:0;
}
return 0;
}
}

自己写的超时,代码为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Solution {
public static int[] getSubarrayBeauty(int[] nums, int k, int x) {
int n = nums.length;
List<Integer> list = new ArrayList<>();
for (int i =0 ;i<n;i++){
list.add(nums[i]);
}
int res[] = new int[n-k+1];
int index = 0;
for (int i =0 ;i<n-k+1;i++){
List<Integer> list1 = new ArrayList<>(list.subList(i, i + k));
Collections.sort(list1);
if (list1.get(x-1)<0){
res[index++]=list1.get(x-1);
}else {
res[index++] = 0;
}
}
return res;
}
}

第4题-使数组所有元素都变为1的最小操作数

image-20230423151058786

示例 1:

1
2
3
4
5
6
7
8
输入:nums = [2,6,3,4]
输出:4
解释:我们可以执行以下操作:

- 选择下标 i = 2 ,将 nums[2] 替换为 gcd(3,4) = 1 ,得到 nums = [2,6,1,4]
- 选择下标 i = 1 ,将 nums[1] 替换为 gcd(6,1) = 1 ,得到 nums = [2,1,1,4]
- 选择下标 i = 0 ,将 nums[0] 替换为 gcd(2,1) = 1 ,得到 nums = [1,1,1,4]
- 选择下标 i = 2 ,将 nums[3] 替换为 gcd(1,4) = 1 ,得到 nums = [1,1,1,1]

示例 2:

1
2
3
输入:nums = [2,10,6,14]
输出:-1
解释:无法将所有元素都变成 1

+++++++++++++

思路:分三种情况讨论:

(1)如果数组中所有的数的最大公约数不为1,则无法变为1

(2)如果数组本来就包含1,则次数为数组的长度-1的个数

(3)找到最小的时最大公约数为1的操作次数,最后的结果为=找到1的最小操作数 + n-1.

代码为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
class Solution {
//分三种情况讨论
public int minOperations(int[] nums) {
int n = nums.length;
//第一种,有1的情况
int count = 0;
for (int i =0 ;i<n;i++){
if (nums[i]!=1) count++;
}
if (count!=n) return count;
//第二种,公约数不为1的情况
int gcd = nums[0];
for (int i = 0;i<n;i++){
gcd = gcd(gcd,nums[i]);
if (gcd==1) break;
}
if (gcd>1) return -1;
//第三种情况
//l:最少几个数能够求出最大公约数为1,
for (int l = 2;l<=n;l++){
//i 到 j 是数组中相邻的l个数
for (int i = 0,j = l-1;j<n;i++,j++){
//求这 i 到 j 这l个数中的最大公约数是否可以求出1
int g = 0;
for (int k = i;k<=j;k++){
g = gcd(nums[k],g);
if (g==1) return l-1+n-1;
}
}
}
return -1;
}
//最大公约数
public int gcd(int a,int b){
while (b!=0){
int t = a%b;
a = b;
b = t;
}
return a;
}
}

342场周赛
http://example.com/2023/04/23/342场周赛/
作者
zlw
发布于
2023年4月23日
许可协议