第 327 场周赛
比赛链接
Date: 2023-01-08
A 正整数和负整数的最大计数
暴力
复杂度分析
- 时间复杂度:\(O(n)\)
- 空间复杂度:\(O(1)\)
B 执行 K 次操作后的最大分数
使用二叉堆维护当前最大值即可。上取整时使用 (x + (3 - 1)) / 3 即可实现上取整操作。
复杂度分析
- 时间复杂度:\(O(k \log n)\)
- 空间复杂度:\(O(n)\)
C 使字符串总不同字符的数目相等
使用哈希表记录每个字符出现的次数,由于每个字符串中的种类数不超过 26,所以可以用 26*26 的双层遍历来枚举替换情况。枚举时,有两种方法:
- 构造哈希表的副本,然后修改后对比即可。这样写不容易写错。(C++ 实现)
- 如果交换的是相同的字母,则跳过。否则根据交换的字母,根据实际的哈希表中的情况计算交换后的种类数进行对比。(容易写错,python 实现)
| class Solution {
public:
bool isItPossible(string word1, string word2) {
unordered_map<char, int> h1, h2;
for (auto c : word1) h1[c]++;
for (auto c : word2) h2[c]++;
for (int i = 0; i < 26; i++) {
for (int j = 0; j < 26; j++) {
char x = 'a' + i;
char y = 'a' + j;
if (h1.count(x) && h2.count(y)) {
auto h1_c = h1, h2_c = h2;
h1_c[x]--; h1_c[y]++;
h2_c[y]--; h2_c[x]++;
int a = 0, b = 0;
for (auto &[k, v] : h1_c) if (v != 0) a++;
for (auto &[k, v] : h2_c) if (v != 0) b++;
if (a == b) return true;
}
}
}
return false;
}
};
|
| class Solution:
def isItPossible(self, word1: str, word2: str) -> bool:
h1, h2 = Counter(word1), Counter(word2)
l1, l2 = len(h1), len(h2)
for x in h1:
for y in h2:
if x == y:
if l1 == l2:
return True
continue
# 计算交换操作之后的种类数
nl1 = l1 - 1 if h1[x] == 1 else l1
nl2 = l2 - 1 if h2[y] == 1 else l2
nl1 = nl1 + 1 if h1[y] == 0 else nl1
nl2 = nl2 + 1 if h2[x] == 0 else nl2
if nl1 == nl2:
return True
return False
|
复杂度分析
- 时间复杂度:\(O(C^4)\),优化后的方法二为 \(O(C^2)\)
- 空间复杂度:\(O(n)\)
D 过桥的时间
工人共有四种状态,每一种状态都存在优先级对比,大致可以分为两类:
- 在左右对岸等待的工人,优先级根据题目中给定进行计算
- 在搬起或者放下的工人,优先级根据完成时间来计算
因此,要设置四个优先队列来分别存储他们。当旧仓库还有货物或者右边还有人时,过程就需要继续:
- 首先,如果搬起或者放下的工人在此时已经完成,则将他们依次加入左边或者右边的等待队列。
- 然后,如果右边有人等待,则取优先级最低的工人过桥,过桥后放入左侧的处于「放下」状态的队列。
- 否则如果旧仓库还有货物,并且左侧有等待的工人,则取优先级最低的工人过桥,过桥后放入右侧处于「搬起」状态的队列,并使得旧仓库待搬运货物数量减一。
- 否则,此时没有人需要过桥,时间应该过渡到第一个处于「放下」或者「搬起」状态的工人切换状态的时刻。
按照上述过程模拟,直到结束即可。注意问题关注的是,最后一个回到左边的工人的到达时间,并不是最后放下货物的时间。
| class Solution {
public:
using PII = pair<int, int>;
int findCrossingTime(int n, int k, vector<vector<int>>& time) {
auto cmp = [&](int x, int y) {
int l = (time[x][0] + time[x][2]);
int r = (time[y][0] + time[y][2]);
return l != r ? l < r : x < y;
};
// 左边和右边的等待队列
priority_queue<int, vector<int>, decltype(cmp)> L(cmp), R(cmp);
// 左边「放下」状态队列和右边「搬起」状态队列,PII 的第一关键字是完成时间,第二关键字是工人ID
priority_queue<PII, vector<PII>, greater<PII>> ql, qr;
// 剩余待处理货物数量和当前时间
int rest = n, cur = 0;
for (int i = 0; i < k; i++) L.push(i);
while (rest > 0 || qr.size() > 0 || R.size() > 0) {
// 1. 如果搬起或者放下的工人在此时已经完成,则将他们依次加入左边或者右边的等待队列。
while (qr.size() && qr.top().first <= cur) {
R.push(qr.top().second);
qr.pop();
}
while (ql.size() && ql.top().first <= cur) {
L.push(ql.top().second);
ql.pop();
}
// 如果右边有人等待,则取优先级最低的工人过桥,过桥后放入左侧的处于「放下」状态的队列。
if (R.size() > 0) {
int x = R.top(); R.pop();
cur += time[x][2];
ql.push({cur + time[x][3], x});
} else if (L.size() > 0 && rest > 0) {
// 否则如果旧仓库还有货物,并且左侧有等待的工人,则取优先级最低的工人过桥,过桥后放入右侧处于「搬起」状态的队列,并使得旧仓库待搬运货物数量减一。
int x = L.top(); L.pop();
cur += time[x][0];
qr.push({cur + time[x][1], x});
rest--;
} else {
// 否则,此时没有人需要过桥,时间应该过渡到第一个处于「放下」或者「搬起」状态的工人切换状态的时刻。
int x = 1e9;
if (qr.size()) x = min(x, qr.top().first);
if (ql.size()) x = min(x, ql.top().first);
if (x != 1e9) cur = max(cur, x);
}
}
return cur;
}
};
|
复杂度分析
- 时间复杂度:\(O(n\log k)\)
- 空间复杂度:\(O(n)\)
本文访问 次