Q1. 连接逆序数组
🔗 https://leetcode.cn/contest/weekly-contest-501/problems/concatenate-array-with-reverse/description/
题目
- 给定长度为 n 的数组 nums,返回长度为 2n 的数组:前半部分为 nums 本身,后半部分为 nums 的逆序。
思路
- 简单数组操作
代码
cpp
class Solution {
public:
vector<int> concatWithReverse(vector<int>& nums) {
vector<int> ans = nums;
int n = nums.size();
for (int i = 0; i < n; i++) {
ans.push_back(nums[n - i - 1]);
}
return ans;
}
};
Q2. 有效单词计数
🔗 https://leetcode.cn/contest/weekly-contest-501/problems/count-valid-word-occurrences/description/
题目
- 将字符串数组 chunks 拼接成字符串 s,再给定查询数组 queries。
- 单词定义:s 中由小写字母组成、连字符仅出现在两个小写字母之间、且不能被更长的合法串包含的极大子串。
- 返回数组 ans,ans[i] 为 queries[i] 作为单词在 s 中出现的次数。
思路
- 碰到 a-z,拼接字符串累积
- 碰到
-,进行前序判断- 若为空,丢弃
- 若为 a-z,拼接上
- - 其他(-),非合法连字符,进行单词统计
- 其他(空格),进行单词统计
代码
cpp
class Solution {
public:
vector<int> countWordOccurrences(vector<string>& chunks, vector<string>& queries) {
stack<char> st;
map<string, int> mp;
auto count = [&](stack<char>& st) {
string key;
while (!st.empty() && st.top() == '-') st.pop();
while (st.empty() == false) {
key.push_back(st.top());
st.pop();
}
reverse(key.begin(), key.end());
mp[key]++;
//printf("mp %s %d\n", key.c_str(), mp[key]);
};
for (string& chunk : chunks) {
for (char& ch : chunk) {
if (ch >= 'a' && ch <= 'z') st.push(ch);
else if (ch == '-') {
if (st.empty()) continue;
char tmp = st.top();
if (tmp >= 'a' && tmp <= 'z') {
st.push(ch);
continue;
} else {
count(st);
}
} else {
count(st);
}
}
}
count(st);
vector<int> ans;
for (string& query : queries) {
ans.push_back(mp[query]);
}
return ans;
}
};
Q3. 可整除替换后的数组最小元素和
题目
- 给定整数数组 nums
- 可以任意多次操作:若 nums[a] % nums[b] == 0,则将 nums[a] 替换为 nums[b]
- 返回操作后数组元素和的最小值。
思路
- 数据范围 10^5,埃拉托斯特尼筛法
- 把 nums 中数字的倍数全部标记,最后用 nums 的原数据查询一下标记数组
代码
cpp
class Solution {
public:
long long minArraySum(vector<int>& nums) {
int max_num = 0;
set<int> s;
for (int num : nums) {
max_num = max(max_num, num);
s.insert(num);
}
vector<int> min_div(max_num + 1, 0);
for (int i = 1; i <= max_num; ++i) {
if (s.count(i)) {
int mark = i;
while (mark <= max_num) {
if (min_div[mark] == 0) {
min_div[mark] = i;
}
mark += i;
}
}
}
long long ans = 0;
for (int num : nums) {
ans += min_div[num];
}
return ans;
}
};
Q4. 购买苹果的最低成本 II
🔗 https://leetcode.cn/contest/weekly-contest-501/problems/minimum-cost-to-buy-apples-ii/description/
题目
- n个商店,苹果价格为
prices[i] - 道路
[u, v, cost, tax]双向,空手走费用cost,携带苹果走费用cost*tax,去程回程路径可不同 - 对每个商店 i,返回获得一个苹果的最小总花费(可直接买,或空手去别处买再带回)。
思路
- 用 cost 图,算第 i 个商店的去程的单源最短路,dijkstra
- 用 cost * tax 图,算第 i 个商店的回程的单源最短路,dijkstra
- 计算第 i 个商店,是直接买,还是去到某个商店(cost 图的单元最短路 + cost * tax 图的单元最短路 + 某个商店 price)的最小值
- 对每个商店都这样计算一遍
代码
cpp
class Solution {
public:
vector<int> minCost(int n, vector<int>& prices, vector<vector<int>>& roads) {
const uint64_t INF = 1e18;
vector<uint64_t> go(n);
vector<uint64_t> back(n);
auto dijkstra = [&](int st, vector<vector<pair<int, uint64_t>>>& edges) {
vector<uint64_t> d(n, INF);
d[st] = 0;
priority_queue<pair<uint64_t, int>, vector<pair<uint64_t, int>>, greater<pair<uint64_t, int>>> pq;
pq.push({0, st});
while (!pq.empty()) {
auto [dist, node] = pq.top(); pq.pop();
if (dist > d[node]) continue;
if (dist >= prices[st]) continue;
for (auto& edge : edges[node]) {
int v = edge.first;
uint64_t c = edge.second;
if (d[v] > dist + c) {
d[v] = dist + c;
pq.push({d[v], v});
}
}
}
return d;
};
vector<vector<pair<int, uint64_t>>> edge1(n); // node, cost
vector<vector<pair<int, uint64_t>>> edge2(n);
for (auto& road : roads) {
int u = road[0];
int v = road[1];
int cost = road[2];
int tax = road[3];
edge1[u].push_back({v, cost});
edge1[v].push_back({u, cost});
edge2[u].push_back({v, cost * (uint64_t)tax});
edge2[v].push_back({u, cost * (uint64_t)tax});
}
vector<int> ans;
for (int i = 0; i < n; i++) {
int min_cost = prices[i];
go = dijkstra(i, edge1);
back = dijkstra(i, edge2);
for (int j = 0; j < n; j++) {
if (go[j] == INF || back[j] == INF) continue;
min_cost = min((uint64_t)min_cost, go[j] + back[j] + prices[j]);
}
ans.push_back(min_cost);
}
return ans;
}
};