[leetcode] 周赛 212

🏆 比赛题目:https://leetcode-cn.com/circle/discuss/QFRzuS/。

按键持续时间最长的键

题目:5546. 按键持续时间最长的键

时间不累加。

class Solution {
public:
    char slowestKey(vector<int>& times, string keys) {
        int n = keys.length();
        char c = keys[0];
        int t = times[0];
        for (int i=1; i<n; i++)
        {
            int t2 = times[i] - times[i-1];
            if (t2 > t || (t2 == t && keys[i] > c)) t=t2, c=keys[i];
        }
        return c;
    }
};

等差子数组

题目:5547. 等差子数组

问就是暴力排序。

class Solution {
public:
    vector<bool> checkArithmeticSubarrays(vector<int>& nums, vector<int>& l, vector<int>& r) 
    {
        int m = l.size(), n = nums.size();
        vector<bool> ans(m, false);
        for (int i=0; i<m; i++)
            ans[i] = check(nums, l[i], r[i]);
        return ans;
    }
    bool check(vector<int> &v, int l, int r)
    {
        assert(l <= r);
        if (l == r) return true;
        vector<int> buf(v.begin()+l, v.begin()+r+1);
        sort(buf.begin(), buf.end());
        int d = buf[1] - buf[0];
        int size = buf.size();
        for (int i=1; i<size; i++)
            if (buf[i] - buf[i-1] != d)
                return false;
        return true;
    }
};

最小体力消耗路径

题目:5548. 最小体力消耗路径

第一直觉是 DFS 枚举每一个路径,记录每个路径的最大差值,然后取最小值。

剪枝:如果某一步的差值大于已找到的 ans ,那么停止搜索。

但是超时了。

这是一类找到「若干最大值中的最小值」的问题。

结合 BFS 和二分查找。

对每一个可能的阈值 m 进行二分查找,从当前位置走到下一位置的条件是 2 位置高度之差小于等于该阈值,按照这样的要求使用 BFS 搜索,如果可以走到 (rows-1, cols-1) 说明阈值可更小,所以 r=m,否则 l=m+1

当然,还有并查集和最短路的解法。只能说 zerotrac 🌸 牛逼!

BFS 和 二分查找

class Solution {
public:
    int rows = -1, cols = -1;
    const vector<vector<int>> dir = {{-1,0}, {0,-1}, {1,0}, {0,1}};
    int minimumEffortPath(vector<vector<int>>& heights) 
    {
        rows = heights.size();
        cols = heights[0].size();
        int l=0, r=(int)1e6;
        while (l<=r)
        {
            if (l == r) break;
            int m = l + (r-l)/2;
            if (bfs(heights, m)) r = m;
            else l = m+1;
        }
        return l;
    }
    bool bfs(vector<vector<int>> &v, int val)
    {
        queue<pair<int,int>> q;
        vector<vector<bool>> vis(rows, vector<bool>(cols, false));
        q.push({0, 0});
        while (!q.empty())
        {
            auto [i,j] = q.front();
            q.pop();
            if (i == rows-1 && j == cols-1) return true;
            for (auto &d: dir)
            {
                int x = i+d[0], y = j+d[1];
                if (x < 0 || y < 0 || x >= rows || y >= cols || vis[x][y]) continue;
                if (abs(v[i][j] - v[x][y]) <= val) q.push({x,y}), vis[x][y] = 1;
            }
        }
        return false;
    }
};

并查集

定义每个格子为一个点,相邻格子的高度之差为边的权值。

通过优先队列,使得边从小到大排序,依次加入并查集,直到 (0,0)(rows-1, cols-1) 相连。在加入并查集的边中的最小值,就是答案。

class Edge 
{
public:
    int x, y, val;
    Edge(int xx, int yy, int vv) : x(xx), y(yy), val(vv) {}
    // 让堆顶是最小的边
    bool operator < (const Edge &e) const { return val > e.val; }
};
class Solution {
public:
    vector<int> root;
    int minimumEffortPath(vector<vector<int>>& heights) 
    {
        int rows = heights.size();
        int cols = heights[0].size();
        int total = rows * cols;
        root.resize(total, -1);
        priority_queue<Edge> q;
        for (int i=0; i<rows; i++)
        {
            for (int j=0; j<cols; j++)
            {
                int id = i*cols+j;
                if (i+1 < rows) q.push(Edge(id, id+cols, abs(heights[i][j] - heights[i+1][j])));
                if (j+1 < cols) q.push(Edge(id, id+1, abs(heights[i][j] - heights[i][j+1])));
            }
        }
        while (!q.empty())
        {
            auto e = q.top();
            q.pop();
            merge(e.x, e.y);
            if (find(0) == find(total-1)) return e.val;
        }
        return 0;
    }
    int find(int x) { return root[x] == -1 ? (x) : (root[x] = find(root[x])); }
    void merge(int x, int y)
    {
        int a = find(x), b = find(y);
        if (a != b) root[b] = a;
    }
};

最后一题是矩阵转换后的秩,看懂就算了。

posted @ 2020-10-25 16:19  sinkinben  阅读(170)  评论(0编辑  收藏  举报