补题与周总结:leetcode第 376 场周赛
复盘与一周总结
wa穿了第3题,赛时其实想到了思路:中位数贪心,从中位数开始,用左右指针找到第一个回文数,与该回文数的代价就是答案。但是没有考虑到左右指针同时找到回文数的情况,wa了一发之后开始改。用一个vector保存代价,只要数组长度大于2就返回其中的较小值。但是没有注意到自己的算法是左右指针同时找,可能出现同一个指针找到两次回文数的情况,此时就不是左右指针分别找到一次回文数。后面改成:数组长度大于5就返回最小值才ac,赛后重写用第一次的思路写了一遍,很快就ac了
现在想想,自己能很快想到正解,但是算法实现的却不是正解,而且赛时还没发现,甚至以为是思路错了,还往平均数那块想了会。只能说,自己在算法实现这块考虑得不仔细吧,下次别着急,想慢点
至于说第4题,赛时完全没有想到中位数贪心,想到哪了呢?我在考虑数组中数的出现次数,出现次数更多的数,最后的代价是否会小于出现次数更小的数。甚至想到:若一个数出现次数超过一半,那么把所有数变为这个数,此时的频率是否最大?接着就对着这个贪心结论证啊证,最后不了了之。但题目的关键点是:操作次数有限,那么就要考虑如何操作能尽可能少的使用操作次数,这样就能想到中位数了
周末这几场打下来,发现自己最大的问题就是:题意的理解。一是读假题,连题目在说什么都没搞清楚,甚至是自以为搞清楚,然后自欺欺人地想算法去了,如abc的E题,小白赛的E题。二是没有抓住题意的重点,只要是稍有难度的题,都需要抓住关键点不断地分析,如这次的第4题,小白赛的F题
问题反而是出现在阅读理解上
2967. 使数组成为等数数组的最小代价(中位数贪心 回文数判断)
2967. 使数组成为等数数组的最小代价 - 力扣(LeetCode)
根据中位数贪心,将数组排序后,从中位数开始,分别向左和向右找到第一个回文数并计算代价,数组两个代价中较小的即可
class Solution {
public:
bool f(string s)
{
int l = 0, r = s.size() - 1;
while (l < r)
{
if (s[l] != s[r]) return false;
l ++ , r -- ;
}
return true;
}
long long minimumCost(vector<int>& nums) {
sort(nums.begin(), nums.end());
int mid;
if (nums.size() & 1) mid = nums[nums.size() / 2];
else mid = (nums[nums.size() / 2] + nums[nums.size() / 2 - 1]) / 2;
long long ans1 = 4e18, ans2 = 4e18;
int l = mid, r = mid;
while (l >= 0)
{
if (f(to_string(l)))
{
long long t = 0;
for (int i = 0; i < nums.size(); ++ i) t += abs(nums[i] - l);
ans1 = t;
break;
}
l -- ;
}
while (r < 2e9)
{
if (f(to_string(r)))
{
long long t = 0;
for (int i = 0; i < nums.size(); ++ i) t += abs(nums[i] - r);
ans2 = t;
break;
}
r ++ ;
}
return min(ans1, ans2);
}
};
2968. 执行操作使频率分数最大(中位数贪心 前缀和 滑窗)
2968. 执行操作使频率分数最大 - 力扣(LeetCode)
要使将数组中的某些数变成同一个数的代价最小,依然是中位数贪心
同时这个序列必须是原序列的一段连续区间。比如原数组为1,2,3,4,将1,2,3变为2的代价一定比1,2,4变为2的代价小
题目要返回代价小于等于k的情况下,最长的连续区间,对于连续区间问题,自然想到滑动窗口
那么接下来要考虑的是窗口滑动时的代价变化,除了
O
(
n
)
O(n)
O(n)暴力求代价,还能使用前缀和进行预处理,
O
(
1
)
O(1)
O(1)地求代价
class Solution {
public:
int maxFrequencyScore(vector<int>& nums, long long k) {
sort(nums.begin(), nums.end());
int n = nums.size();
vector<long long> a(n + 1), s(n + 1);
for (int i = 0; i < n; ++ i) a[i + 1] = nums[i], s[i + 1] = s[i] + a[i + 1];
auto f = [&](int l, int mid, int r) -> long long{
long long left = (mid - l) * a[mid] - (s[mid - 1] - s[l - 1]);
long long right = (s[r] - s[mid - 1]) - (r - mid + 1) * a[mid];
return left + right;
};
int l = 1, r = 1;
int ans = 0;
while (r <= n)
{
while (f(l, (l + r) / 2, r) > k) l ++ ;
ans = max(ans, r - l + 1);
r ++ ;
}
return ans;
}
};