题目链接
题目解析
A.6200. 处理用时最长的那个任务的员工
AC代码
class Solution {
public:
int hardestWorker(int n, vector<vector<int>>& logs) {
vector<int> cnt(n, 0);
int ans = -1, ansCnt = -1;
int last = 0, t;
for (auto &arr : logs) {
t = arr[1] - last;
if (t > ansCnt) {
ansCnt = t;
ans = arr[0];
} else if (t == ansCnt && arr[0] < ans) {
ans = arr[0];
}
last = arr[1];
}
return ans;
}
};
思路分析
其实是一道很简单的题目,但是我没有认真读题竟然还wa了两次,觉得非常不应该。一直以来觉得第一道题非常简单于是写的时候都没有想太多,今天抱着同样的态度结果没有搞清楚题目意思就开始写,wa两次的罚时远远大于认真思考的时间。一定要认真谨慎,不管在面对什么问题。
B.6201. 找出前缀异或的原始数组
AC代码
class Solution {
public:
vector<int> findArray(vector<int>& pref) {
int n = pref.size();
vector<int> ans(n);
ans[0] = pref[0];
for (int i = 1; i < n; ++i) {
ans[i] = pref[i] ^ pref[i - 1];
}
return ans;
}
};
思路分析
根据异或的性质很快可以得出ans[i]=pref[i]^pref[i - 1]
C.6202. 使用机器人打印字典序最小的字符串
AC代码
class Solution {
public:
string robotWithString(string s) {
int n = s.size();
vector<pair<char, int>> cnt(n);
cnt[n - 1] = make_pair(s[n - 1], n - 1);
for (int i = n - 2; i >= 0; --i) {
if (s[i] <= cnt[i + 1].first) {
cnt[i] = make_pair(s[i], i);
} else {
cnt[i] = cnt[i + 1];
}
}
string t;
t.push_back('z' + 1);
string ans;
int idx = 0;
while (idx < n) {
auto [c, small] = cnt[idx];
if (c < t.back()) {
for (int i = idx; i < small; ++i) t.push_back(s[i]);
ans.push_back(c);
idx = small + 1;
} else {
ans.push_back(t.back());
t.pop_back();
}
}
int nn = t.size();
for (int i = nn - 1; i > 0; --i) ans.push_back(t[i]);
return ans;
}
};
思路分析
一个贪心+模拟,主要的贪心策略就是:为当前位置放置可以放置的最小字符。这个字符的来源有两个:一个是s,一个是t,如果在s中,那么我们就要将最小字符前面的字符放在t中,而t只能取最后一个字符。为了确定来自哪里,我们从后往前处理出s当前位置后面最小的字符是哪个
初始情况下s中的任意字符都比t小,为了避免复杂的判断,这里技巧性地给t中先验性地填入了一个最大字符(’z’ + 1)。总体来说还是实现的比较优雅的。
D.6203. 矩阵中和能被 K 整除的路径
AC代码
class Solution {
public:
int numberOfPaths(vector<vector<int>>& grid, int k) {
using ll = long long;
static constexpr ll MOD = 1e9 + 7;
int n = grid.size();
int m = grid[0].size();
vector<vector<ll>> dp(m, vector<ll>(k));
auto update0 = [&](vector<ll> &now, int t) {
vector<ll> last(k);
for (int i = 0; i < k; ++i) {
last[(i + t) % k] += now[i];
}
now.swap(last);
};
auto update1 = [&](vector<ll> &now, const vector<ll> &last, int t) {
for (int i = 0; i < k; ++i) {
int tmp = (i + t) % k;
now[tmp] += last[i];
if (now[tmp] > MOD) {
now[tmp] %= MOD;
}
}
};
auto deal = [&](int x, int y, int dir) {
if (dir == 0) {
//从上方更新
update0(dp[y], grid[x][y]);
} else {
//从左方更新
update1(dp[y], dp[y - 1], grid[x][y]);
}
};
dp[0][grid[0][0] % k] = 1;
for (int j = 1; j < m; ++j) {
deal(0, j, 1);
}
for (int i = 1; i < n; ++i) {
deal(i, 0, 0);
for (int j = 1; j < m; ++j) {
deal(i, j, 0);
deal(i, j, 1);
}
}
return dp[m - 1][0];
}
};
思路分析
很浓重的dp色彩,每一个位置从上边或者左边转移而来。可能朴素的做法是维护所有路径的和,但是由于我们要判断的是是否整除,因此技巧性地不断对和取余,维护的是k的剩余系,这样有效地缩小了每个状态的大小。
除此之外,由于我们是按照从左到右,从上到下的搜索顺序,所以我们可以利用滚动数组优化空间复杂度,只保存每一行的状态,然后优先从上边更新(也就是自身),再从左边更新。
自己在优先的时间内还能够想到这种优化空间复杂度的做法,的确是很机智。
整体代码的实现也是比较好的,用lambda表达式封装更新动作。
就是在初始状态的更新上犯了一点点错误。而且还把1e9+7写成了109+7,我记得我之前就因为这个问题wa过,这次又犯。。。主要是题面给的就是109+7,我吐了。害得我debug好久才发现这个问题。
周赛总结
优点
- 思路敏捷,代码实现快速,代码结构清晰
缺点
- 轻视简单题造成不必要的罚时
- 因为细节问题导致出错,应该再小心谨慎些
改进方案
小心谨慎,注意题面,注意数据范围,注意初始状态