A 对角线最长的矩形的面积
模拟
class Solution {
public:
int areaOfMaxDiagonal(vector<vector<int>> &dimensions) {
int res = 0, len2 = 0;
for (auto &x: dimensions)
if (x[0] * x[0] + x[1] * x[1] > len2 || x[0] * x[0] + x[1] * x[1] == len2 && x[0] * x[1] > res) {
res = x[0] * x[1];
len2 = x[0] * x[0] + x[1] * x[1];
}
return res;
}
};
B 捕获黑皇后需要的最少移动次数
枚举:只有两种情况可以1次移动就能攻击黑皇后:1)白象和黑皇后在一条对角线或斜对角线上且白车没有在中间。 2)白车和黑皇后在同一行或同一列且白象没有在中间。 其他情况都要两次移动才能攻击黑皇后
class Solution {
public:
int minMovesToCaptureTheQueen(int a, int b, int c, int d, int e, int f) {
if (a == e && (c != e || d < min(b, f) || d > max(b, f)))
return 1;
if (b == f && (d != f || c < min(a, e) || c > max(a, e)))
return 1;
if (c - d == e - f && (a - b != e - f || a < min(c, e) || a > max(c, e)))
return 1;
if (c + d == e + f && (a + b != e + f || a < min(c, e) || a > max(c, e)))
return 1;
return 2;
}
};
C 移除后集合的最多元素数
贪心:分别统计两个数组中各数出现的频率,然后先从 n u m s 1 nums1 nums1 中删除 n / 2 n/2 n/2 个数,优先删在 n u m s 1 nums1 nums1 出现次数大于 1 的数,其次删在 n u m s 2 nums2 nums2 中有出现的数,最后删其他的数。然后再从 n u m s 2 nums2 nums2 中删除 n / 2 n/2 n/2 个数,优先删在 n u m s 2 nums2 nums2 出现次数大于 1 的数,其次删在当前 s s s 中有出现的数,最后删其他的数。
class Solution {
public:
int maximumSetSize(vector<int> &nums1, vector<int> &nums2) {
unordered_map<int, int> cnt1, cnt2;
for (auto x: nums1)
cnt1[x]++;
for (auto x: nums2)
cnt2[x]++;
int n = nums1.size();
unordered_set<int> res;
{
int rm = n / 2;
for (auto &[v, f]: cnt1)
if (f != 1) {
int d = min(rm, f - 1);
f -= d;
rm -= d;
}
if (rm != 0) {
for (auto &[v, f]: cnt1)
if (f && cnt2.count(v)) {
f = 0;
if (--rm == 0)
break;
}
}
if (rm != 0) {
for (auto &[v, f]: cnt1)
if (f) {
f = 0;
if (--rm == 0)
break;
}
}
for (auto &[v, f]: cnt1)
if (f != 0)
res.insert(v);
}
int rm = n / 2;
for (auto &[v, f]: cnt2) {
if (f != 1) {
int d = min(rm, f - 1);
f -= d;
rm -= d;
}
}
if (rm != 0) {
for (auto &[v, f]: cnt2)
if (f && res.count(v)) {
f = 0;
if (--rm == 0)
break;
}
}
if (rm != 0) {
for (auto &[v, f]: cnt2)
if (f) {
f = 0;
if (--rm == 0)
break;
}
}
for (auto &[v, f]: cnt2)
if (f != 0)
res.insert(v);
return res.size();
}
};
D 执行操作后的最大分割数量
前缀和 + 二分 + 动态规划 + 枚举:设 p s [ i ] [ j ] ps[i][j] ps[i][j] 为 s [ 0 , i − 1 ] s[0,i-1] s[0,i−1] 中 ( ′ a ′ + j ) ('a'+j) (′a′+j) 字符出现的个数,设 p [ i ] p[i] p[i] 为字符串 s [ i , s . s i z e ( ) − 1 ] s[i,s.size()-1] s[i,s.size()−1] 不执行替换操作按题目要求的最大分割数量。可以逆序求 p [ i ] p[i] p[i] :通过二分求包含 k k k 个不同字符的最长前缀 s [ i , l ] s[i,l] s[i,l] ,则有 p [ i ] = p [ l + 1 ] + 1 p[i]=p[l+1]+1 p[i]=p[l+1]+1 。然后正序枚举各位替换成各字符的情况,枚举过程中记录已遍历过的字符串前缀的分割数量,以及当前分割中出现过哪些字符。
class Solution {
public:
int maxPartitionsAfterOperations(string s, int k) {
int n = s.size();
int ps[n + 1][26];
for (int j = 0; j < 26; j++)
ps[0][j] = 0;
for (int i = 0; i < n; i++)
for (int j = 0; j < 26; j++)//计算前缀和
ps[i + 1][j] = s[i] - 'a' == j ? ps[i][j] + 1 : ps[i][j];
int p[n + 1];
p[n] = 0;
for (int i = n - 1; i >= 0; i--) {//求p数组
vector<int> vis(26, -1);
int l = i, r = n - 1;
while (l < r) {
int mid = (l + r + 1) / 2;
int cnt = 0;
for (int j = 0; j < 26; j++)
if (ps[mid + 1][j] - ps[i][j] != 0)
cnt++;
if (cnt <= k)
l = mid;
else
r = mid - 1;
}
p[i] = p[l + 1] + 1;
}
int res = p[0];//不替换的情况
int vis = 0;//当前分割中出现过的字符的 mask
int cnt_pre = 0;//已遍历过的字符串前缀的分割数量
for (int i = 0; i < n; i++) {//枚举各位
for (int j = 0; j < 26; j++) {//枚举s[i]替换成 'a'+j 的情况
int tmp = vis | (1 << j);// 将'a'+j 加入 mask
if (pop_cnt(tmp) > k) {// 'a'+j 与当前分割不在同一分割
tmp = 1 << j;//'a'+j 所在新的分割的 mask
int l = i, r = n - 1;
while (l < r) {//二分求 'a'+j 所在分割的右边界
int mid = (l + r + 1) / 2;
int cnt = 0;
for (int j = 0; j < 26; j++)
if (tmp >> j & 1 || ps[mid + 1][j] - ps[i + 1][j] != 0)
cnt++;
if (cnt <= k)
l = mid;
else
r = mid - 1;
}
res = max(res, cnt_pre + 1 + 1 + p[l + 1]);
} else {// 'a'+j 与当前分割在同一分割
int l = i, r = n - 1;
while (l < r) {//二分求当前分割的右边界
int mid = (l + r + 1) / 2;
int cnt = 0;
for (int j = 0; j < 26; j++)
if (tmp >> j & 1 || ps[mid + 1][j] - ps[i + 1][j] != 0)
cnt++;
if (cnt <= k)
l = mid;
else
r = mid - 1;
}
res = max(res, cnt_pre + 1 + p[l + 1]);
}
}
//更新当前分割的 mask
vis |= 1 << (s[i] - 'a');
if (pop_cnt(vis) > k) {
cnt_pre++;//更新已遍历过的字符串前缀的分割数量
vis = 1 << (s[i] - 'a');
}
}
return res;
}
int pop_cnt(int x) {//返回二进制表示中1的位数
int res = 0;
for (int i = 0; i < 32; i++)
if (x >> i & 1)
res++;
return res;
}
};