第 316 场周赛 - 力扣(LeetCode)
https://leetcode.cn/contest/weekly-contest-316/
这次周赛只做了两道题,对于困难题的思考还是不太熟练,在看了灵神的视频讲解后,自己梳理了一下思路写出了代码。
6214. 判断两个事件是否存在冲突
题目描述:
给你两个字符串数组 event1 和 event2 ,表示发生在同一天的两个闭区间时间段事件,其中:
event1 = [startTime1, endTime1] 且
event2 = [startTime2, endTime2]
事件的时间为有效的 24 小时制且按 HH:MM 格式给出。
当两个事件存在某个非空的交集时(即,某些时刻是两个事件都包含的),则认为出现 冲突 。
如果两个事件之间存在冲突,返回 true ;否则,返回 false 。
题目解析:
直接通过字符串的比较即可,如果两个事件之间不存在交集的话,那么要么第二个事件的开始时间大于等于第一个事件的结束时间,要么第一个事件的开始时间大于等于第二个时间的结束时间,如果满足这两种情况中的一种就返回false,否则返回true,代码如下:
class Solution {
public:
bool haveConflict(vector<string>& event1, vector<string>& event2) {
return !(event1[1]<event2[0]||event1[0]>event2[1]);
}
};
执行用时:4 ms, 在所有 C++ 提交中击败了100.00%的用户
内存消耗:10.7 MB, 在所有 C++ 提交中击败了100.00%的用户
6224. 最大公因数等于K的子数组数目:
题目描述:
给你一个整数数组 nums 和一个整数 k ,请你统计并返回 nums 的子数组中元素的最大公因数等于 k 的子数组数目。
子数组 是数组中一个连续的非空序列。
数组的最大公因数 是能整除数组中所有元素的最大整数。
题目解析:
因为数据范围的原因,暴力计算枚举即可,代码如下:
class Solution {
public:
int cal(int a , int b){
int temp = a;
while(temp!=0){
temp = a%b;
a = b;
b = temp;
}
return a;
}
int subarrayGCD(vector<int>& nums, int k) {
int cnt = 0;
for(int i = 0 ; i<nums.size()-1 ; i++){
if(nums[i]==k)cnt++;
int temp = nums[i];
for(int j = i+1 ; j<nums.size() ; j++){
temp = cal(temp,nums[j]);
if(temp==k)cnt++;
}
}
if(nums[nums.size()-1]==k)cnt++;
return cnt;
}
};
执行用时:28 ms, 在所有 C++ 提交中击败了100.00%的用户
内存消耗:8.6 MB, 在所有 C++ 提交中击败了100.00%的用户
优化方法:假设一组数为1,2,3,3,6,9,从右往左进行子数组选择对应的最大公因数为1,1,3,3,3,9。可以看到对应的公因数从左到右是非递减的有序数列,我们使用一个数组来记录某个数字及左边数字的公因数数组,并记录对应的序号,对数组进行去重操作,用一个标记位来记录最近一个不满足为K的倍数的数,如果某个数不为K的倍数,那么将记录公因数的数组清空,更新标记位。如果该数组第一个数等于K,则将该数对应的序号减去上一个不满足为K的倍数的数的序号,加在结果上。代码如下:
class Solution {
public:
int subarrayGCD(vector<int>& nums, int k) {
vector<pair<int,int>> data;
int i0 = -1;
int res = 0;
for(int i = 0 ; i<nums.size() ; i++){
if(nums[i]%k){
data.clear();
i0 = i;
continue;
}
data.push_back({nums[i],i});
int j = 0 ;
for(auto &p : data){
p.first = gcd(p.first,nums[i]);
if(data[j].first==p.first){
data[j].second = p.second;
}
else{
j++;
data[j] = p;
}
}
data.erase(data.begin()+j+1,data.end());
if(data[0].first==k)res += data[0].second-i0;
}
return res;
}
};
执行用时:4 ms, 在所有 C++ 提交中击败了100.00%的用户
内存消耗:8.6 MB, 在所有 C++ 提交中击败了100.00%的用户
2448. 使数组相等的最小开销
题目描述:
给你两个下标从 0 开始的数组 nums 和 cost ,分别包含 n 个 正 整数。
你可以执行下面操作 任意 次:
将 nums 中 任意 元素增加或者减小 1 。
对第 i 个元素执行一次操作的开销是 cost[i] 。
请你返回使 nums 中所有元素 相等 的 最少 总开销。
题目解析:
根据数字的大小对nums和cost进行排序,这里有一个方法,一个数的cost为x,那么其实就相当于x个这个数的cost为1,只需要将其进行扩展然后查找中位数,因为一组数距离中位数的距离之和是最低的,将这个中位数作为目标数进行答案计算即可,代码如下:
// 中位数 开贪
class Solution {
public:
long long minCost(vector<int>& nums, vector<int>& cost) {
long long sumcost = 0;
int len = nums.size();
for(int i = 0 ; i<len ; i++){
sumcost += cost[i];
}
vector<pair<int,int>> arr(len);
for(int i = 0 ; i <len ; i++){
arr[i] = {nums[i],cost[i]};
}
sort(arr.begin() , arr.end() , [](const pair<int,int> &a , const pair<int,int> &b)->bool{
return a.first<b.first;
});
long long temp = 0;
for(int i = 0; i<len ; i++){
temp += arr[i].second;
if(temp>sumcost/2){
temp = arr[i].first;
break;
}
}
long long res = 0;
for(int i = 0 ; i<len ; i++){
res += abs(temp-arr[i].first)*1LL*arr[i].second;
}
return res;
}
};
执行用时:60 ms, 在所有 C++ 提交中击败了100.00%的用户
内存消耗:38.8 MB, 在所有 C++ 提交中击败了100.00%的用户
还有一种方法就是枚举,但是因为数据量的原因,暴力枚举会导致超时,所以这里用到了类似前缀和的一个思想,还是先进行排序,计算所有数的代价总和sumcost,然后计算以第一个数为目标数的代价和med,如果我要换为以第二个数为目标数,代价和需要变小(sumcost-cost[0])*(nums[1]-nums[0]),代价和变大cost[0]*(nums[1]-nums[0])那么设num[1]-nums[0]=d,这个变化总导致变小了(sumcost-2*cost[0])*d,然后计算res(初始值等于med)和med变小过后的值中的最小值即可,进行遍历计算,代码如下:
// 暴力枚举+前缀和
class Solution {
public:
long long minCost(vector<int>& nums, vector<int>& cost) {
int len = nums.size();
vector<pair<int,int>> arr(len);
for(int i = 0 ; i<len ; i++){
arr[i] = {nums[i],cost[i]};
}
sort(arr.begin(),arr.end(),[](const pair<int,int> &a , const pair<int,int> &b)->bool{
return a.first<b.first;
});
long long res = 0;
long long med = 0;
long long sumcost = 0;
for(int i = 0 ; i<len ; i++){
res += (arr[i].first-arr[0].first)*1LL*arr[i].second;
sumcost += arr[i].second;
}
med = res;
for(int i = 1 ; i<len ; i++){
sumcost -= 2*arr[i-1].second;
med -= sumcost*(arr[i].first-arr[i-1].first);
res = min(res , med);
}
return res;
}
};
执行用时:64 ms, 在所有 C++ 提交中击败了100.00%的用户
内存消耗:38.7 MB, 在所有 C++ 提交中击败了100.00%的用户
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即可,代码如下:
class Solution {
public:
long long makeSimilar(vector<int>& nums, vector<int>& target) {
vector<int> pos = {0,0};
sort(nums.begin(),nums.end());
sort(target.begin(),target.end());
long long res = 0;
for(int i = 0 ; i<nums.size() ; i++){
int temp = nums[i]%2;
int &j = pos[temp];
while(target[j]%2!=temp)++j;
res += abs(nums[i]-target[j++]);
}
return res/4;
}
};
执行用时:172 ms, 在所有 C++ 提交中击败了100.00%的用户
内存消耗:76.8 MB, 在所有 C++ 提交中击败了100.00%的用户