深信服笔试(C++)
第一题:貌似是计算一下每个字符有多少段,答案就是大于n段的字符数量。
第二题:编辑距离leetcode原题,dp一下即可。
第三题:生命游戏,直接模拟即可,注意上下边界是相邻的。
第四题:题目大意是最后分成两个子序列,使得两边子序列之和最大值最小?
刚开始写了个错解,过了60%,后面想一下感觉挺容易的。考虑二分答案,我们枚举断开点,看左边子序列和小于等于二分的值的元素个数最多能有多少个,然后看右边子序列和小于等于二分的值的元素个数最多能,若这两边相加大于等于k个,说明这个答案一定可以。那么我们需要怎么进行求解答案呢?其实求左边和求右边道理都一样,那么我们不妨求左边,我们把每个a[i]看成价值,我们最大可以拿二分的答案值,那么就可以基于反悔贪心的思想去拿到最大的个数,复杂度是nlognlogn
#include "bits/stdc++.h" using namespace std; using ll = long long; void solve() { int n, k; cin >> n >> k; vector<int> a(n); for (int i = 0; i < n; i++) { cin >> a[i]; } auto check = [&](ll v) { vector<int> l(n); priority_queue<int> Q; ll rv = v; for (int i = 0; i < n; i ++) { if (rv >= a[i]) { rv -= a[i]; Q.push(a[i]); } else { if (!Q.empty() && Q.top() > a[i]) { rv += Q.top(); Q.pop(); rv -= a[i]; Q.push(a[i]); } } l[i] = Q.size(); } while (Q.size()) { Q.pop(); } rv = v; for (int i = n - 1; i >= 0; i--) { if (rv >= a[i]) { rv -= a[i]; Q.push(a[i]); } else { if (!Q.empty() && Q.top() > a[i]) { rv += Q.top(); Q.pop(); rv -= a[i]; Q.push(a[i]); } } if (Q.size() + (i > 0 ? l[i - 1] : 0) >= k) { return true; } } return false; }; ll l = 1, r = 1e18; while (l <= r) { ll m = (l + r) >> 1; if (check(m)) { r = m - 1; } else { l = m + 1; } } cout << l << '\n'; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr), cout.tie(nullptr); int _ = 1; cin >> _; while (_--) { solve(); } return 0; }#深信服笔试#