第 230 场周赛
Problem A - 统计匹配检索规则的物品数量
按要求模拟即可。
- 时间复杂度。
- 空间复杂度。
参考代码(C++)
class Solution {
public:
int countMatches(vector<vector<string>>& items, string ruleKey, string ruleValue) {
int ans = 0;
int n = items.size();
unordered_map<string, int> mp = {{"type", 0}, {"color", 1}, {"name", 2}};
int key = mp[ruleKey];
for (int i = 0; i < n; ++i)
if (items[i][key] == ruleValue)
ans++;
return ans;
}
};
Problem B - 最接近目标价格的甜点成本
本题数据范围很小,暴力枚举辅料组合就可以通过,但时间复杂度为指数级。
把问题转化为背包问题,可以将时间复杂度降低到多项式级别。
- 因为每种辅料最多可以用两次,所以直接把每种辅料变成两个。
- 基料必须且只能选一种,可以首先处理好。
之后就按照0-1背包问题的一般做法,依次枚举辅料即可。
- 时间复杂度。其中为背包的最大容量。本题中,因为答案不可能超过。
- 空间复杂度。
参考代码(C++)
class Solution {
public:
int closestCost(vector<int>& baseCosts, vector<int>& toppingCosts, int target) {
vector<bool> can(20001);
for (int base : baseCosts)
can[base] = true;
toppingCosts.insert(toppingCosts.end(), toppingCosts.begin(), toppingCosts.end());
for (int topping : toppingCosts) {
for (int i = 20000; i >= topping; --i)
can[i] = can[i] || can[i - topping];
}
int min_gap = INT_MAX, ans = 0;
for (int i = 1; i <= 20000; ++i)
if (can[i] && abs(i - target) < min_gap) {
ans = i;
min_gap = abs(i - target);
}
return ans;
}
};
Problem C - 通过最少操作次数使数组的和相等
我们可以发现:
- 如果则无解,因为此时即使把数少的那边都变成,同时数多的那边都变成,也不能使两侧相等。
- 增大中的一个数,就等于减小中的一个数。
- 在有多个数可以选择的情况下,增大时应该优先增大较小的数(上升空间大);同理,减小时应当优先减小较大的数(下降空间大)。
因此,我们统计出增长空间和减小空间。
- 如果,我们应当进行减小操作(减小中的数或增大中的数),直到。
- 反之,我们进行增大操作,直到。
直接对或降序排序就可以通过;而利用计数排序,我们可以将时间复杂度进一步优化到线性时间。
- 时间复杂度。
- 空间复杂度。
参考代码(C++)
class Solution {
public:
int minOperations(vector<int>& nums1, vector<int>& nums2) {
int n = nums1.size(), m = nums2.size();
if (n > m * 6 || m > n * 6)
return -1;
int s1 = 0, s2 = 0;
vector<int> inc(6), dec(6);
for (int num : nums1) {
s1 += num;
if (num < 6)
inc[6 - num]++;
if (num > 1)
dec[num - 1]++;
}
for (int num : nums2) {
s2 += num;
if (num < 6)
dec[6 - num]++;
if (num > 1)
inc[num - 1]++;
}
if (s1 == s2)
return 0;
int cnt = 0;
if (s1 > s2) {
for (int i = 5; i >= 1; --i) {
while (dec[i]) {
s1 -= i;
dec[i]--;
cnt++;
if (s1 <= s2)
return cnt;
}
}
} else {
for (int i = 5; i >= 1; --i) {
while (inc[i]) {
s1 += i;
inc[i]--;
cnt++;
if (s1 >= s2)
return cnt;
}
}
}
return -1;
}
};
Problem D - 车队 II
因为每次发生相遇之后,都可能发生速度的变化,所以我们需要按照时间顺序来处理所有的事件。
方法一:优先队列+并查集
- 时间复杂度。
- 空间复杂度。
参考代码(C++)
struct DSU {
vector<int> p, sz, speed, left, right;
vector<double> pos, speed_change;
DSU(vector<int> &speed, vector<double> &pos): speed(speed), pos(pos) {
int n = speed.size();
p = vector<int>(n);
left = vector<int>(n);
right = vector<int>(n);
speed_change = vector<double>(n);
for (int i = 0; i < n; ++i)
p[i] = left[i] = right[i] = i;
sz = vector<int>(n, 1);
}
int find(int i) {
return p[i] == i ? i : p[i] = find(p[i]);
}
void merge(int i, int j) {
int pi = find(i), pj = find(j);
if (pi == pj)
return;
int spd = min(speed[pi], speed[pj]);
int l = min(left[pi], left[pj]);
int r = max(right[pi], right[pj]);
double ps = max(pos[pi], pos[pj]);
double spc = max(speed_change[pi], speed_change[pj]);
if (sz[pi] >= sz[pj]) {
speed[pi] = spd;
left[pi] = l;
right[pi] = r;
pos[pi] = ps;
speed_change[pi] = spc;
sz[pi] += sz[pj];
p[pj] = pi;
} else {
speed[pj] = spd;
left[pj] = l;
right[pj] = r;
pos[pj] = ps;
speed_change[pj] = spc;
sz[pj] += sz[pi];
p[pi] = pj;
}
}
int get_speed(int idx) {
return speed[find(idx)];
}
double get_pos(int idx) {
return pos[find(idx)];
}
int get_left(int idx) {
return left[find(idx)];
}
int get_right(int idx) {
return right[find(idx)];
}
double get_speed_change(int idx) {
return speed_change[find(idx)];
}
void set_pos(int idx, double ps) {
pos[find(idx)] = ps;
}
void set_speed_change(int idx, double spc) {
speed_change[find(idx)] = spc;
}
void move(int idx, double t) {
if (t < get_speed_change(idx))
return;
double delta = t - get_speed_change(idx);
pos[find(idx)] += get_speed(idx) * delta;
speed_change[find(idx)] = t;
}
};
class Solution {
public:
vector<double> getCollisionTimes(vector<vector<int>>& cars) {
int n = cars.size();
vector<double> ans(n, -1.0);
priority_queue<pair<double, int>, vector<pair<double, int>>, greater<>> pq;
vector<int> speed(n);
vector<double> time(n, -1.0), speed_change(n), pos(n);
for (int i = 0; i < n; ++i)
pos[i] = cars[i][0], speed[i] = cars[i][1];
for (int i = 0; i < n - 1; ++i) {
if (cars[i][1] > cars[i + 1][1]) {
double t = (pos[i + 1] - pos[i]) / (cars[i][1] - cars[i + 1][1]);
pq.emplace(t, i);
time[i] = t;
}
}
DSU dsu(speed, pos);
while (!pq.empty()) {
auto [t, idx] = pq.top();
pq.pop();
if (abs(t - time[idx]) > 1e-6 || ans[idx] > 0)
continue;
ans[idx] = t;
int L = dsu.get_left(idx) - 1, R = dsu.get_right(idx + 1) + 1;
dsu.move(idx, t), dsu.move(idx + 1, t);
dsu.merge(idx, idx + 1);
if (L >= 0 && ans[L] < 0) {
dsu.move(L, t), dsu.move(L + 1, t);
int sp0 = dsu.get_speed(L), sp1 = dsu.get_speed(L + 1);
if (sp0 > sp1) {
double nxt = t + (dsu.get_pos(L + 1) - dsu.get_pos(L)) / (sp0 - sp1);
pq.emplace(nxt, L);
time[L] = nxt;
} else {
time[L] = -1.0;
}
}
if (R < n && ans[R - 1] < 0) {
dsu.move(R - 1, t), dsu.move(R, t);
int sp0 = dsu.get_speed(R - 1), sp1 = dsu.get_speed(R);
if (sp0 > sp1) {
double nxt = t + (dsu.get_pos(R) - dsu.get_pos(R - 1)) / (sp0 - sp1);
pq.emplace(nxt, R - 1);
time[R - 1] = nxt;
} else {
time[R - 1] = -1.0;
}
}
}
return ans;
}
};