第 67 场双周赛
Problem A - 找到和最大的长度为 K 的子序列
方法一:排序
先排序找出最大的个数,然后再将它们按照原数组中的位置排序。
- 时间复杂度。
- 空间复杂度。
参考代码(Python 3)
class Solution:
    def maxSubsequence(self, nums: List[int], k: int) -> List[int]:
        return [nums[i] for i in sorted([x[1] for x in sorted([(num, i) for i, num in enumerate(nums)])[-k:]])]
Problem B - 适合打劫银行的日子
方法一:动态规划
从左到右递推找出每个数左边最长不上升子串的长度,再从右到左递推找出每个数右边最长不上升子串的长度;左右长度都满足的即为好日子。
- 时间复杂度。
- 空间复杂度。
参考代码(C++)
class Solution {
public:
    vector<int> goodDaysToRobBank(vector<int>& security, int time) {
        int n = security.size();
        vector<int> l(n), r(n);
        for (int i = 1; i < n; ++i)
            if (security[i] <= security[i - 1])
                l[i] = l[i - 1] + 1;
        for (int i = n - 2; i >= 0; --i)
            if (security[i] <= security[i + 1])
                r[i] = r[i + 1] + 1;
        vector<int> ans;
        for (int i = 0; i < n; ++i)
            if (l[i] >= time && r[i] >= time)
                ans.emplace_back(i);
        return ans;
    }
};
Problem C - 引爆最多的炸弹
方法一:BFS或DFS
首先根据每个炸弹的位置和引爆范围,计算出它可以直接引爆哪些其他炸弹,然后连边。注意这里的边是有向边,因为A可以引爆B,不代表B可以引爆A。
之后就是每个炸弹都作为源点BFS/DFS一次,求出所有答案中的最大值即可。
- 时间复杂度。
- 空间复杂度。
参考代码(C++)
using ll = long long;
ll dis(int x1, int y1, int x2, int y2) {
    return 1LL * (x1 - x2) * (x1 - x2) + 1LL * (y1 - y2) * (y1 - y2);
}
class Solution {
public:
    int maximumDetonation(vector<vector<int>>& bombs) {
        int n = bombs.size();
        vector<vector<int>> adj(n);
        for (int i = 0; i < n; ++i) {
            auto &p = bombs[i];
            for (int j = 0; j < n; ++j) {
                auto &q = bombs[j];
                if (dis(p[0], p[1], q[0], q[1]) <= 1LL * p[2] * p[2])
                    adj[i].emplace_back(j);
            }
        }
        
        int ans = 1;
        for (int i = 0; i < n; ++i) {
            vector<bool> vis(n);
            vis[i] = true;
            queue<int> q;
            q.push(i);
            int cnt = 1;
            while (!q.empty()) {
                int u = q.front();
                q.pop();
                for (int v : adj[u]) {
                    if (!vis[v]) {
                        cnt++;
                        vis[v] = true;
                        q.push(v);
                    }
                }
            }
            ans = max(ans, cnt);
        }
        
        return ans;
    }
};
方法二:位优化Floyd
在建立有向图后,我们可以使用位优化的Floyd来进行第二步计算。
- 时间复杂度,其中为字长。
- 空间复杂度。
参考代码(C++)
using ll = long long;
using bs = bitset<100>;
ll dis(int x1, int y1, int x2, int y2) {
    return 1LL * (x1 - x2) * (x1 - x2) + 1LL * (y1 - y2) * (y1 - y2);
}
class Solution {
public:
    int maximumDetonation(vector<vector<int>>& bombs) {
        int n = bombs.size();
        vector<bs> d(n);
        for (int i = 0; i < n; ++i) {
            d[i].set(i);
            auto &p = bombs[i];
            for (int j = 0; j < n; ++j) {
                auto &q = bombs[j];
                if (dis(p[0], p[1], q[0], q[1]) <= 1LL * p[2] * p[2])
                    d[i].set(j);
            }
        }
        
        for (int mid = 0; mid < n; ++mid) 
            for (int s = 0; s < n; ++s) {
                if (d[s][mid])
                    d[s] |= d[mid];
            }
        
        int ans = 0;
        for (int i = 0; i < n; ++i)
            ans = max(ans, (int)d[i].count());
        
        return ans;
    }
};
Problem D - 序列顺序查询
方法一:平衡树
本题中需要使用支持查询第位元素的平衡树。set或multiset是不行的,因为不支持按位查询。不过我们还有PBDS(Policy-based data structures),在力扣上也是可以直接使用的。
注意这里比较函数用了less,而分数要从大到小,所以这里分数取相反数后再插入。
当然,其他的平衡树也都是可以的,Splay、Treap、红黑树……有模板的可以用模板,不过手写的话就要慢一点了。
- 插入和查询的时间复杂度都为。
- 空间复杂度。
参考代码(C++)
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp> 
using namespace __gnu_pbds;
template<class T>
using ordered_set = tree<T, null_type, less<T>, rb_tree_tag, tree_order_statistics_node_update>;
class SORTracker {
    ordered_set<pair<int, string>> s;
    int counter = 0;
public:
    SORTracker() {}
    
    void add(string name, int score) {
        s.insert(make_pair(-score, name));
    }
    
    string get() {
        return s.find_by_order(counter++)->second;
    }
};
方法二:双set
注意到本题中的查询操作并不是随机进行的,而是按照严格递增的方式,我们可以采用类似数据流中中位数的方式,维护两个set来实现本题要求的功能。
- 插入的时间复杂度都为,查询的时间复杂度在均摊意义下为。
- 空间复杂度。
参考代码(C++)
class SORTracker {
    set<pair<int, string>, greater<>> s1;
    set<pair<int, string>> s2;
    int counter = 0;
public:
    SORTracker() {}
    
    void add(string name, int score) {
        s1.emplace(-score, name);
        if (*s1.begin() > *s2.begin()) {
            s2.insert(*s1.begin());
            s1.erase(s1.begin());
        }
    }
    
    string get() {
        counter++;
        while (s1.size() > counter) {
            s2.insert(*s1.begin());
            s1.erase(s1.begin());
        }
        while (s1.size() < counter) {
            s1.insert(*s2.begin());
            s2.erase(s2.begin());
        }
        return s1.begin()->second;
    }
};