第 357 场力扣周赛题解

A 故障键盘

在这里插入图片描述

简单模拟

class Solution {
public:
    string finalString(string s) {
        string res;
        for (auto c: s)
            if (c != 'i')
                res.push_back(c);
            else
                reverse(res.begin(), res.end());
        return res;
    }
};

B 判断是否能拆分数组

在这里插入图片描述

区间dp:定义 p i , j p_{i,j} pi,j表示子数组 n u m s [ i , j ] nums[i,j] nums[i,j]能否满足条件地拆分为 j − i + 1 j-i+1 ji+1个非空数组, p i , j p_{i,j} pi,j最多可由 p i + 1 , j p_{i+1,j} pi+1,j p i , j − 1 p_{i,j-1} pi,j1转移,注意特判 n u m s [ i , j ] nums[i,j] nums[i,j]为整个数组的情况。

class Solution {
public:
    bool canSplitArray(vector<int> &nums, int m) {
        int n = nums.size();
        int s[n + 1];//前缀和
        s[0] = 0;
        for (int i = 1; i <= n; i++)
            s[i] = s[i - 1] + nums[i - 1];
        int p[n][n];//1:可满足条件地拆分,0:不可满足条件地拆分
        for (int len = 1; len <= n; len++)
            for (int i = 0, j = i + len - 1; j < n; i++, j++) {
                if (len == 1)//长度为1满足条件
                    p[i][j] = 1;
                else//当前区间和>=m 或当前区间为整个数组,才可能可以满足条件地拆分
                    p[i][j] = s[j + 1] - s[i] >= m || len == n ? (p[i][j - 1] | p[i + 1][j]) : 0;
            }
        return p[0][n - 1];
    }
};

C 找出最安全路径

在这里插入图片描述
在这里插入图片描述

多源 b f s bfs bfs+二分:以所有小偷所在位置为源点跑多源 b f s bfs bfs,这样就求出了矩阵各个位置的安全系数,然后二分枚举答案,设当前枚举值为 r e s res res,判断当前枚举值是否可行:通过 b f s bfs bfs判断 ( 0 , 0 ) (0,0) (0,0) ( n − 1 , n − 1 ) (n-1,n-1) (n1,n1)之间是否存在这样的路径,使得该路径上任意位置的安全系数都不小于 r e s res res

class Solution {
public:
    int maximumSafenessFactor(vector<vector<int>> &grid) {
        int n = grid.size();
        int d[n][n];//记录位置的安全系数
        memset(d, -1, sizeof(d));//初始化未访问标志
        queue<pair < int, int> > q;
        for (int i = 0; i < n; i++)
            for (int j = 0; j < n; j++)
                if (grid[i][j]) {//多源bfs的源点
                    d[i][j] = 0;
                    q.emplace(i, j);
                }
        int dr[4] = {1, -1, 0, 0};
        int dc[4] = {0, 0, 1, -1};
        while (!q.empty()) {//多源bfs
            auto [r, c] = q.front();
            q.pop();
            for (int k = 0; k < 4; k++) {
                int nr = r + dr[k];
                int nc = c + dc[k];
                if (nr < 0 || nr >= n || nc < 0 || nc >= n || d[nr][nc] != -1)
                    continue;
                d[nr][nc] = d[r][c] + 1;
                q.emplace(nr, nc);
            }
        }
        int l = 0, r = 2 * (n - 1);
        int vis[n][n];//记录是否在当前枚举值的bfs过程中访问过
        memset(vis, -1, sizeof(vis));
        while (l < r) {//二分枚举答案
            int res = (l + r + 1) / 2;
            queue<pair < int, int> > q;
            if (d[0][0] >= res) {//(0,0)为源点
                vis[0][0] = res;
                q.emplace(0, 0);
            }
            while (!q.empty()) {//bfs判断当前枚举值是否可行
                auto [r, c] = q.front();
                q.pop();
                if (r == n - 1 && c == n - 1)
                    break;
                for (int k = 0; k < 4; k++) {
                    int nr = r + dr[k];
                    int nc = c + dc[k];
                    if (nr < 0 || nr >= n || nc < 0 || nc >= n || d[nr][nc] < res || vis[nr][nc] == res)
                        continue;
                    vis[nr][nc] = res;//标记当前枚举值的bfs过程中已访问
                    q.emplace(nr, nc);
                }
            }
            if (vis[n - 1][n - 1] == res)
                l = res;
            else
                r = res - 1;
        }
        return l;
    }
};

D 子序列最大优雅度

在这里插入图片描述
在这里插入图片描述

堆+哈希:将 i t e m s items items按利润降序排序,然后将前 k k k个项目加入选择集合,然后枚举剩余的项目 i t e m s [ i ] items[i] items[i]

  • i t e m s [ i ] items[i] items[i]的项目类别在选择集合中已有,则直接跳过该项目
  • i t e m s [ i ] items[i] items[i]的项目类别没有在选择集合中
    • 若当前选择集合中存在出现次数大于1的项目,将其中利润最小的项目移出集合,同时将 i t e m s [ i ] items[i] items[i]加入集合
    • 若当前选择集合中不存在出现次数大于1的项目,结束枚举

实现可以用堆来维护选择集合中利润最小的项目,用哈希表记录各个类别在当前选择集合中的出现次数。

class Solution {
public:
    long long findMaximumElegance(vector<vector<int>> &items, int k) {
        sort(items.begin(), items.end(), [](const vector<int> &a, const vector<int> &b) { return a[0] > b[0]; });//按利润降序排序
        unordered_map<int, int> cnt;
        long long s = 0;//选择集合中项目的利润和
        priority_queue<pair<int, int>, vector<pair<int, int>>, greater<>> minheap;
        for (int i = 0; i < k; i++) {
            minheap.emplace(items[i][0], items[i][1]);
            s += items[i][0];
            cnt[items[i][1]]++;
        }
        long long res = s + (long long) (cnt.size() * cnt.size());
        for (int i = k; i < items.size(); i++) {
            int pi = items[i][0], ci = items[i][1];
            if (cnt[ci])//当前项目类别在选择集合中已有
                continue;
            while (!minheap.empty() && cnt[minheap.top().second] == 1)
                minheap.pop();
            if (minheap.empty())//当前选择集合中不存在出现次数大于1的项目
                break;
            auto [top_pi, top_ci] = minheap.top();
            minheap.pop();
            cnt[top_ci]--;
            cnt[ci]++;
            s += pi - top_pi;
            res = max(res, s + (long long) (cnt.size() * cnt.size()));
        }
        return res;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值