赞
踩
链接:https://leetcode.cn/contest/weekly-contest-307/
日期:20220821
模拟遍历一遍,求过程中的最小值就行了
class Solution {
public:
int minNumberOfHours(int a, int b, vector<int>& c, vector<int>& d) {
int s1 = 0, s2 = 0;
int n = d.size();
for (int i = 0; i < n; i++) {
s1 = max(c[i] + 1 - a, s1);
a -= c[i];
s2 = max(d[i] + 1 - b, s2);
b += d[i];
}
return s1 + s2;
}
};
统计每个数字的个数,然后贪心的构造就行了。
class Solution { public: string largestPalindromic(string num) { vector<int> cnts(10); for (auto c : num) { cnts[c - '0']++; } string s; for (int i = 9; i >= 0; i--) { if (i == 0 && s.size() == 0) continue; if (cnts[i] / 2 != 0) { s += string(cnts[i] / 2, '0' + i); } cnts[i] %= 2; } string k = s; reverse(k.begin(), k.end()); for (int i = 9; i >= 0; i--) { if (cnts[i]) { s += i + '0'; break; } } return s + k; } };
重新建图,一次BFS或者DFS即可。
dfs + 链式前向星写法
const int N = 100010; int h[N], nxt[N * 2], e[N * 2], idx; class Solution { public: void add(int a, int b) { nxt[idx] = h[a]; e[idx] = b; h[a] = idx++; } void dfs1(TreeNode *root) { if (root->left) { add(root->val, root->left->val); add(root->left->val, root->val); dfs1(root->left); } if (root->right) { add(root->val, root->right->val); add(root->right->val, root->val); dfs1(root->right); } } int cnt = 0; int mx = 0; void dfs2(int r, int p) { if (p != -1) { cnt++; mx = max(mx, cnt); } for (int x = h[r]; ~x; x = nxt[x]) { int b = e[x]; if (b == p) continue; dfs2(b, r); } cnt--; } int amountOfTime(TreeNode* root, int start) { memset(h, -1, N * 4); idx = 0; dfs1(root); cout << idx << endl; queue<int> que; dfs2(start, -1); return mx; } };
这道题非常难,只有100多个选手做出来。
比赛时想到用归并来做,但是没写出代码。因为即使归并也有长度限制,需要一定的编码技巧。事后看讲解,即使归并也未必能做出来。
思路如下:n 个数的子序列有
2
n
2^n
2n 种可能。因为每一个数都有选和不选两种可能。假设 前 i -1 个数,枚举出的子序列长度为 p,那么第 i 个数会导致这个序列长度变成 2p。幸运的是,我们只需要 前 k 大的数即可。此外我们可以在第一次进行排序,后面都归并得到新序列,并再次取得前 k 个数即可。这样我们得复杂度是 O(nk)。
2000
×
100000
=
2
×
1
0
8
2000 × 100000 = 2×10^8
2000×100000=2×108必然会超时。
其实最大值是可以轻易得到得:所有正数相加即可。其他的数可以看作从最大的数中减去正数或者 加上负数。我们可以把所有的数看成是正数。然后求其第k - 1 小值,用最大的正数减去他即可。
那有什么变化呢?取第 k- 1 小值我们可以笃定,从前 k 小值中取就行。这样就不用遍历整个数组了。如果不理解可以做一个还算比较恰当的类比:在图论中,求最短路径有很多算法,但是求最长路径,却是一个 np-hard 问题。因此往往将最大转换成求最小,会将问题变得简单许多。
这样问题复杂度变成
O
(
k
2
)
O(k^2)
O(k2) 就可没问题了。
代码如下
typedef long long LL; class Solution { public: long long kSum(vector<int>& nums, int k) { LL sum = 0; vector<LL> arr; // 把nums处理为负数 for (auto c : nums) { if (c >= 0) { arr.push_back(-c); sum += c; // 求最大值 } else { arr.push_back(c); } } sort(arr.begin(), arr.end(), greater<LL>()); // 处理为负数后,排一下序,让最大的负数(绝对值最小)在前面 if (arr.size() > k) arr.erase(arr.begin() + k, arr.end()); vector<LL> a, b; // 归并用,a放归并序列1,另一个归并序列就是a的平移。b是归并的目标序列 a.push_back(sum); // 最大值在前面 for (auto c : arr) { int i = 0, j = 0; // 归并的目标序列数量最多为k。 while (b.size() < k && i < a.size() && j < a.size()) { if (a[i] > a[j] + c) { b.push_back(a[i++]); } else { b.push_back(a[j++] + c); } } while (b.size() < k && i < a.size()) b.push_back(a[i++]); while (b.size() < k && j < a.size()) b.push_back(a[j++] + c); // 交换一下a,b。a继承归并的结果 std::swap(a, b); b.clear(); } return a[k - 1]; //第k个值就是结果 } };
第二种写法
class Solution { public: typedef long long LL; long long kSum(vector<int>& nums, int k) { int n = nums.size(); vector<int> arr; LL mx = 0; for (auto x : nums) { if (x < 0) { arr.push_back(-x); } else { arr.push_back(x); mx += x; } } if (k == 1) return mx; sort(arr.begin(), arr.end()); if (arr.size() > k) arr.erase(arr.begin() + k, arr.end()); vector<LL> a, b; a.push_back(0); // 技巧 最大值放前面 for (auto x : arr) { int i = 0, j = 0; while (b.size() < k && i < a.size() && j < a.size()) { if (a[i] < a[j] + x) { b.push_back(a[i++]); } else { b.push_back(a[j++] + x); } } while(b.size() < k && i < a.size()) b.push_back(a[i++]); while(b.size() < k && j < a.size()) b.push_back(a[j++] + x); std::swap(a, b); b.clear(); } return mx - a[k - 1]; } };
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。