两道贪心题
文章目录
3106. 满足距离约束且字典序最小的字符串
字典序:abcd顺序,其中z和a的差值是1。
distance(s, t)
表示对应位置字符的ASCLL差的累加。
例如distance(“ab”, “cd”) == 4 (c - a + d - b
)
当k等于0时,表示s和t完全一样
当k等于1时,表示只有s[i] != t[i]
,且s[i] - t[i] == 1
所以相当于求对字符串s操作k次,每次k减一,字符移动一位,最小字典项的字符串是什么。
尽量使得每一个位置的字符为a
class Solution {
public String getSmallestString(String s, int k) {
// 要求字典序最小,肯定是从前往后调整
char[] chs = s.toCharArray();
for (int i = 0; i < chs.length; ++i) {
char c = chs[i];
int dis = Math.min(c - 'a', 'z' + 1 - c);
if (dis > k) {
// 当c距离a的最小距离超过k时,直接令c往前跳k步,为什么不是往后跳呢?
// 因为两者的最小值都大于k了,往后跳肯定跳不到a
chs[i] = (char)(chs[i] - k); // 往前跳是为了保证字典项最小
break;
}
k -= dis;
chs[i] = 'a';
}
return new String(chs);
}
}
3107. 使数组中位数等于 K 的最少操作数
中位数小于K
中位数大于K
class Solution {
public long minOperationsToMakeMedianK(int[] nums, int k) {
int mid = nums.length / 2;
Arrays.sort(nums);
long ans = 0;
if (nums[mid] > k) { // nums[mid]表示当前数组的中位数
// 将中间左边的位置置为小于等于k
// 为什么不需要管中间右边的元素呢,因为右边的元素一定大于等于k
// 为什么只需要设置为k呢,因为题目要求操作次数最小
for (int i = mid; i >= 0; i--) {
if (nums[i] <= k) break;
ans += nums[i] - k;
}
} else if (nums[mid] < k) {
// 将中间右边的元素置为大于等于k
for (int i = mid; i < nums.length; ++i) {
if (nums[i] >= k) break;
ans += k - nums[i];
}
}
return ans;
}
}
思考:改成输入 $10^5 $个询问,每个询问包含一个 k k k,如何高效地回答每个询问?
首先也是先排序,然后判断中位数是否大于k
如果中位数大于k:就二分找到第一个大于k的位置,假设是i,从i位置到mid位置所有元素都变为k,所需要的操作次数=
sum[mid] - sum[i - 1] - (mid - i + 1) * k
,其中sum是前缀和数组。class Solution { public long minOperationsToMakeMedianK(int[] nums, int k) { int mid = nums.length / 2; Arrays.sort(nums); long ans = 0; long[] sum = new long[nums.length]; sum[0] = nums[0]; for (int i = 1; i < nums.length; ++i) { sum[i] = sum[i - 1] + nums[i]; } if (nums[mid] > k) { // 将中间左边的位置置为小于等于k // 为什么不需要管中间右边的元素呢,因为右边的元素一定大于等于k // 为什么只需要设置为k呢,因为操作次数最小 int l = 0, r = nums.length - 1; while (l < r) { int m = l + r >> 1; if (nums[m] > k) { r = m; } else { l = m + 1; } } if (l <= mid) { if (l == 0) { ans += sum[mid] - ((long)mid - l + 1) * k; } else { ans += sum[mid] - sum[l - 1] - ((long)mid - l + 1) * k; } } } else if (nums[mid] < k) { // 将中间右边的元素置为大于等于k int l = 0, r = nums.length - 1; while (l < r) { int m = l + r + 1 >> 1; if (nums[m] < k) { l = m; } else { r = m - 1; } } if (l >= mid) { if (mid == 0) { // l - mid + 1) * k可能会溢出,所以要强转为long ans += ((long)l - mid + 1) * k - sum[l]; } else { ans += ((long)l - mid + 1) * k - (sum[l] - sum[mid - 1]); } } } return ans; } }