得到 0 的操作数
签到题,模拟操作即可。
class Solution {
public int countOperations(int num1, int num2) {
int res = 0;
for (; num1 * num2 != 0; res++) {
if (num1 > num2) {
num1 -= num2;
} else {
num2 -= num1;
}
}
return res;
}
}
使数组变成交替数组的最少操作数
统计每种数字在奇数、偶数下标上的数量,然后使用最多的和次多的作为最终数字即可。
class Solution {
public int minimumOperations(int[] nums) {
Map<Integer, Integer> cnt0 = new HashMap<>(); // 偶数下标计数
Map<Integer, Integer> cnt1 = new HashMap<>(); // 奇数下标计数
for (int i = 0; i < nums.length; i++) {
if (i % 2 == 0) {
cnt0.put(nums[i], cnt0.getOrDefault(nums[i], 0) + 1);
} else {
cnt1.put(nums[i], cnt1.getOrDefault(nums[i], 0) + 1);
}
}
int[] cnt0Max = getMax(cnt0);
int[] cnt1Max = getMax(cnt1);
if (cnt0Max[0] != cnt1Max[0]) {
return nums.length - cnt0.getOrDefault(cnt0Max[0], 0) - cnt1.getOrDefault(cnt1Max[0], 0);
}
return nums.length - Math.max(
cnt0.getOrDefault(cnt0Max[0], 0) + cnt1.getOrDefault(cnt1Max[1], 0),
cnt0.getOrDefault(cnt0Max[1], 0) + cnt1.getOrDefault(cnt1Max[0], 0)
);
}
private int[] getMax(Map<Integer, Integer> cnt) {
int[] res = new int[2];
for (var c : cnt.keySet()) {
if (cnt.getOrDefault(res[0], 0) <= cnt.get(c)) {
res[1] = res[0];
res[0] = c;
} else if (cnt.getOrDefault(res[1], 0) <= cnt.get(c)) {
res[1] = c;
}
}
return res;
}
}
拿出最少数目的魔法豆
前缀和。排序后,枚举分界点,分界点之前的全部置零,分界点之后的全部置为与分界点处相同的数目,通过前缀和可以快速计算。
class Solution {
public long minimumRemoval(int[] beans) {
Arrays.sort(beans);
var sum = new IntervalSum(beans);
long res = sum.suffix(0) - (long) beans[0] * (long) beans.length;
for (int i = 0; i < beans.length - 1; i++) {
// [0, i] -> 0; [i+1, length) -> beans[i + 1]
res = Math.min(res, sum.prefix(i) + sum.suffix(i + 1) - (long) beans[i + 1] * (beans.length - i - 1));
}
return res;
}
}
class IntervalSum {
private final long[] pre;
public IntervalSum(int[] arr) {
pre = new long[arr.length];
pre[0] = arr[0];
for (int i = 1; i < arr.length; i++) {
pre[i] = pre[i - 1] + arr[i];
}
}
// interval [0, i]
public long prefix(int i) {
return interval(0, i + 1);
}
// == interval [i, length)
public long suffix(int i) {
return interval(i, pre.length);
}
// [start, end)
public long interval(int start, int end) {
end--;
return pre[end] - (start == 0 ? 0 : pre[start - 1]);
}
}
数组的最大与和
记忆化搜索,令 f[i]
表示 slots 状态为 i 时,还能获取到多少加和
状态转移:枚举当前数字放到哪个 slot 里面
答案:f[0]
其中 i 是一个 18 位的二进制数,每两位表示一个 slot 里已经放入了多少个数字
class Solution {
public int maximumANDSum(int[] nums, int numSlots) {
Map<Integer, Integer> mem = new HashMap<>();
return maximumANDSum(0, nums, numSlots, nums.length, mem);
}
private int maximumANDSum(int stat, int[] nums, int numSlots, int numLeft, Map<Integer, Integer> mem) {
if (mem.containsKey(stat)) {
return mem.get(stat);
}
if (numLeft == 0) {
return 0;
}
int curRes = 0;
for (int i = 1; i <= numSlots; i++) {
int slot = getSlot(stat, i);
if (slot == 2) {
continue;
}
int and = i & nums[numLeft - 1];
int stat2 = setSlot(stat, i, slot + 1);
curRes = Math.max(curRes, and + maximumANDSum(stat2, nums, numSlots, numLeft - 1, mem));
}
mem.put(stat, curRes);
return curRes;
}
// i start from 1
private int getSlot(int bits, int i) {
int offset = (i - 1) * 2;
return (bits >> offset) & 0b11;
}
// num = 0 or 1 or 2
private int setSlot(int bits, int i, int num) {
int offset = (i - 1) * 2;
bits -= bits & (0b11 << offset);
bits += num << offset;
return bits;
}
}
\