6224. 最大公因数等于 K 的子数组数目
给你一个整数数组 nums
和一个整数 k
,请你统计并返回 nums
的子数组中元素的最大公因数等于 k
的子数组数目。
子数组 是数组中一个连续的非空序列。
数组的最大公因数 是能整除数组中所有元素的最大整数。
输入:nums = [9,3,1,2,6,3], k = 3 输出:4 解释:nums 的子数组中,以 3 作为最大公因数的子数组如下: - [9,3,1,2,6,3] - [9,3,1,2,6,3] - [9,3,1,2,6,3] - [9,3,1,2,6,3]
本题主要考察知识点:
(1)最大公因数,可使用algorithm库中的函数__gcd(x,y)。
其中__gcd(x,y)推导,可以使用辗转相除法【即两数相除,取余数重复进行相除,直到余数为0时,前一个除数即为最大公约数】
287=91*3+14 // 287%91 余14
91=14*6+7 // 91%14 余7
14=7*2 // 14%7 余0 即最后一个除数为最大公因数
inline int gcd(int a,int b){
if(a%b==0)
return b;
else return (gcd(b,a%b));
}
库函数__gcd 同样道理。
class Solution {
public:
int gcd(int a,int b){
return a%b?gcd(b,a%b):b;
}
int subarrayGCD(vector<int>& nums, int k) {
int n=nums.size();
int res=0;
for(int i=0;i<n;i++){
if(nums[i]%k==0){
//只有子数组的首个元素是k的倍数的元素,才可能存在连续子数组的最大公因数为k
int x=0;
for(int j=i;j<n;j++){
x=gcd(x,nums[j]);//求得元素组的最大公因数是x,判断x是否为k
//下一个元素代入,判断和前面的子数组元素(最大公因数x)间的最大公因数是多少,如 9 9 3 4 9 3
if(x==k)//若是,则是一个子数组
res++;
}
}
}
return res;
}
};
6216. 使数组相等的最小开销
给你两个下标从 0 开始的数组 nums
和 cost
,分别包含 n
个 正 整数。
你可以执行下面操作 任意 次:
- 将
nums
中 任意 元素增加或者减小1
。
对第 i
个元素执行一次操作的开销是 cost[i]
。
请你返回使 nums
中所有元素 相等 的 最少 总开销。
输入:nums = [1,3,5,2], cost = [2,3,1,14] 输出:8 解释:我们可以执行以下操作使所有元素变为 2 : - 增加第 0 个元素 1 次,开销为 2 。 - 减小第 1 个元素 1 次,开销为 3 。 - 减小第 2 个元素 3 次,开销为 1 + 1 + 1 = 3 。 总开销为 2 + 3 + 3 = 8 。 这是最小开销。
将nums排序后,各点落入坐标位置如图。若使各点到target位置操作和最小(距离最小),那么target就应该是中位数。从两侧往内侧剖析,nums最小值和最大值到target距离和应是固定值(max-min),那么内侧点皆是如此;假设个数为偶数,落在了最中间,但是不影响target线移动到最内侧的上下某一个点上,个数是奇数的话,则是落在最中间那个点。以上白话实际就是为了说明以下数学问题:差的平方和 Σ(x-t)^2 最小时,t 为平均值;差的绝对值之和 Σ|x-t| 最小时,t 为中位数。
以上是拓展知识,而该题目则是对每个nums数都附加了权重,实际可以理解为每个nums[i],重复出现了cost[i],这样就和上面的问题大同小异了。
//朴素版本
class Solution {
public:
long long minCost(vector<int>& nums, vector<int>& cost) {
vector<pair<int,int>>a;
long long all=0;
int n=nums.size();
for(int i=0;i<n;i++){
a.push_back(pair<int,int>(nums[i], cost[i]));
all+=cost[i];
}
sort(a.begin(),a.end(),[&](pair<int,int>&a1,pair<int,int>&a2){
return a1.first<a2.first;
});
long long pd=0,mid=all/2;//mid是中位数的位置(数目的一半)
for(int i=0;i<n;i++){
pd+=a[i].second;
if(pd>=mid){
mid=a[i].first;//mid继续利用,记录中位数
break;
}
}
//以上动作寻找中位数是nums哪一位
long long res=0;
for(auto m:a){
res+=(long)abs(m.first-mid)*(long)m.second;
}
return res;
}
};
6217. 使数组相似的最少操作次数
给你两个正整数数组 nums
和 target
,两个数组长度相等。
在一次操作中,你可以选择两个 不同 的下标 i
和 j
,其中 0 <= i, j < nums.length
,并且:
- 令
nums[i] = nums[i] + 2
且 - 令
nums[j] = nums[j] - 2
。
如果两个数组中每个元素出现的频率相等,我们称两个数组是 相似 的。
请你返回将 nums
变得与 target
相似的最少操作次数。测试数据保证 nums
一定能变得与 target
相似。
贪心算法:
两个数组从小到大排序,小对小,大对大进行操作;
其次,偶数对偶数,奇数对奇数进行操作;
每对元素的差值总和用操作 +(-) 2来弥补,因为每次可以操作差值为4,所以res/4是最终结果。
class Solution {
public:
long long makeSimilar(vector<int>& nums, vector<int>& target) {
sort(nums.begin(),nums.end());
sort(target.begin(),target.end());
long long res=0;
int pd[2]={0,0};
for(auto m:nums){
int n=m%2;
//偶数则pd[0]当做下标变化,奇数则pd[1]当做下标变化
while(n!=target[pd[n]]%2) pd[n]++;//找target中下一个与nums同类型的
res+=abs(m-target[pd[n]++]);
}
return res/4;
}
};