第 87 场双周赛

82 阅读5分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

6184. 统计共同度过的日子数

Alice 和 Bob 计划分别去罗马开会。

给你四个字符串 arriveAlice ,leaveAlice ,arriveBob 和 leaveBob 。Alice 会在日期 arriveAlice 到 leaveAlice 之间在城市里(日期为闭区间),而 Bob 在日期 arriveBob 到 leaveBob 之间在城市里(日期为闭区间)。每个字符串都包含 5 个字符,格式为 "MM-DD" ,对应着一个日期的月和日。

请你返回 Alice和 Bob 同时在罗马的天数。

你可以假设所有日期都在 同一个 自然年,而且 不是 闰年。每个月份的天数分别为:[31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] 。

 

示例 1:

输入:arriveAlice = "08-15", leaveAlice = "08-18", arriveBob = "08-16", leaveBob = "08-19"
输出:3
解释:Alice 从 8 月 15 号到 8 月 18 号在罗马。Bob 从 8 月 16 号到 8 月 19 号在罗马,他们同时在罗马的日期为 8 月 16、17 和 18 号。所以答案为 3 。

示例 2:

输入:arriveAlice = "10-01", leaveAlice = "10-31", arriveBob = "11-01", leaveBob = "12-31"
输出:0
解释:Alice 和 Bob 没有同时在罗马的日子,所以我们返回 0 。

提示:

所有日期的格式均为 "MM-DD" 。 Alice 和 Bob 的到达日期都 早于或等于 他们的离开日期。 题目测试用例所给出的日期均为 非闰年 的有效日期。

思路

  1. 解析字符串成为月和日
  2. 计算之间的间隔日期,不在同一个月的日期分开计算

代码

class Solution {
public:
    
    pair<int, int> topair(string s) {
        return {stoi(s.substr(0, 2)), stoi(s.substr(3, 2))};
    }
    int countDaysTogether(string arriveAlice, string leaveAlice, string arriveBob, string leaveBob) {
        int dates[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
        auto a = topair(arriveAlice);
        auto b = topair(leaveAlice);
        auto x = topair(arriveBob);
        auto y = topair(leaveBob);
        if (b < x || a > y) return 0;
        else {
            auto begin = max(a, x);
            auto end = min(b, y);
            int ans = 0;
            while (begin.first != end.first) {
                int t = begin.first;
                ans += dates[t] - begin.second + 1;
                begin.first ++;
                begin.second = 1;
            } 
            ans += end.second - begin.second + 1;
            return ans;
        }
        return 0;
    }
};

6185. 运动员和训练师的最大匹配数

给你一个下标从 0 开始的整数数组 players ,其中 players[i] 表示第 i 名运动员的 能力 值,同时给你一个下标从 0 开始的整数数组 trainers ,其中 trainers[j] 表示第 j 名训练师的 训练能力值 。

如果第 i 名运动员的能力值 小于等于 第 j 名训练师的能力值,那么第 i 名运动员可以 匹配 第 j 名训练师。除此以外,每名运动员至多可以匹配一位训练师,每位训练师最多可以匹配一位运动员。

请你返回满足上述要求 players 和 trainers 的 最大 匹配数。

 

示例 1:

输入:players = [4,7,9], trainers = [8,2,5,8]
输出:2
解释:
得到两个匹配的一种方案是:
- players[0] 与 trainers[0] 匹配,因为 4 <= 8 。
- players[1] 与 trainers[3] 匹配,因为 7 <= 8 。
可以证明 2 是可以形成的最大匹配数。

示例 2:

输入:players = [1,1,1], trainers = [10]
输出:1
解释:
训练师可以匹配所有 3 个运动员
每个运动员至多只能匹配一个训练师,所以最大答案是 1

 

提示:

1 <= players.length, trainers.length <= 105
1 <= players[i], trainers[j] <= 109

思路

贪心的想法
先排序,然后用当前最小的去匹配一个最小的,匹配不到,则证明失败

代码

class Solution {
public:
    int matchPlayersAndTrainers(vector<int>& players, vector<int>& trainers) {
        sort(begin(players), end(players));
        sort(begin(trainers), end(trainers));
        int ans = 0;
        for (int i = 0, j = 0; i < players.size() && j < trainers.size(); i ++, j ++) {
            while (j < trainers.size() && trainers[j] < players[i]) j ++;
            if (j < trainers.size() && trainers[j] >= players[i]) ans ++;
            else break;
        }
        return ans;
    }
};

6186. 按位或最大的最小子数组长度

给你一个长度为 n 下标从 0 开始的数组 nums ,数组中所有数字均为非负整数。对于 0 到 n - 1 之间的每一个下标 i ,你需要找出 nums 中一个 最小 非空子数组,它的起始位置为 i (包含这个位置),同时有 最大 的 按位或运算值 。

换言之,令 Bij 表示子数组 nums[i...j] 的按位或运算的结果,你需要找到一个起始位置为 i 的最小子数组,这个子数组的按位或运算的结果等于 max(Bik) ,其中 i <= k <= n - 1 。 一个数组的按位或运算值是这个数组里所有数字按位或运算的结果。

请你返回一个大小为 n 的整数数组 answer,其中 answer[i]是开始位置为 i ,按位或运算结果最大,且 最短 子数组的长度。

子数组 是数组里一段连续非空元素组成的序列。

 

示例 1:

输入:nums = [1,0,2,1,3]
输出:[3,3,2,2,1]
解释:
任何位置开始,最大按位或运算的结果都是 3 。
- 下标 0 处,能得到结果 3 的最短子数组是 [1,0,2] 。
- 下标 1 处,能得到结果 3 的最短子数组是 [0,2,1] 。
- 下标 2 处,能得到结果 3 的最短子数组是 [2,1] 。
- 下标 3 处,能得到结果 3 的最短子数组是 [1,3] 。
- 下标 4 处,能得到结果 3 的最短子数组是 [3] 。
所以我们返回 [3,3,2,2,1]

示例 2:

输入:nums = [1,2]
输出:[2,1]
解释:
下标 0 处,能得到最大按位或运算值的最短子数组长度为 2 。
下标 1 处,能得到最大按位或运算值的最短子数组长度为 1 。
所以我们返回 [2,1]

 

提示:

n == nums.length
1 <= n <= 105
0 <= nums[i] <= 109

思路

直接做不好做,无法求出连续的一段相与之后的值
考虑位运算上的值
对于每一个数,都在二进制位置上对应上1时,则覆盖了最大值的这一位,所以,我们当前的最大值只需要找出每一位上最早出现的下标,就可以完成相与得到最大值
根据这样的思路,我们利用队列来维护一下每一位上的“与的序列”即可

代码

class Solution {
public:
    vector<int> smallestSubarrays(vector<int>& nums) {
        deque<int> arr[32];
        for (int i = 0; i < nums.size(); i ++) 
            for (int k = 0; k < 32; k ++) 
                if (nums[i] >> k & 1) arr[k].push_back(i);
        vector<int> ans;
        for (int i = 0; i < nums.size(); i ++) {
            int res = i;
            for (int k = 0; k < 32; k ++) 
                res = max(res, arr[k].front());
            ans.push_back(res - i + 1);
            for (int k = 0; k < 32; k ++) 
                if (nums[i] >> k & 1) arr[k].pop_front();
        }
        return ans;
    }
};

6187. 完成所有交易的初始最少钱数

给你一个下标从 0 开始的二维整数数组 transactions,其中transactions[i] = [costi, cashbacki] 。

数组描述了若干笔交易。其中每笔交易必须以 某种顺序 恰好完成一次。在任意一个时刻,你有一定数目的钱 money ,为了完成交易 i ,money >= costi 这个条件必须为真。执行交易后,你的钱数 money 变成 money - costi + cashbacki 。

请你返回 任意一种 交易顺序下,你都能完成所有交易的最少钱数 money 是多少。

 

示例 1:

输入:transactions = [[2,1],[5,0],[4,2]]
输出:10
解释:
刚开始 money = 10 ,交易可以以任意顺序进行。
可以证明如果 money < 10 ,那么某些交易无法进行。

示例 2:

输入:transactions = [[3,0],[0,3]]
输出:3
解释:
- 如果交易执行的顺序是 [[3,0],[0,3]] ,完成所有交易需要的最少钱数是 3 。
- 如果交易执行的顺序是 [[0,3],[3,0]] ,完成所有交易需要的最少钱数是 0 。
所以,刚开始钱数为 3 ,任意顺序下交易都可以全部完成。

  提示:

1 <= transactions.length <= 105
transactions[i].length == 2
0 <= costi, cashbacki <= 109

思路

要满足所有条件下均成立的最小值
我们就可以去找返利,找到最坏情况下的最小值即可
什么时候是最坏情况呢? 我们把钱分成两种:

  1. x > y
  2. x < y
    对于1来说,我们的钱会越来越少
    对于2来说,我们的钱会越来越多
    所以,我们尽可能的先使用1,然后使用2
    在使用1时,尽可能的使用1里面 y较小的
    使用2时,尽可能使用x较大的

代码

class Solution {
public:
    long long minimumMoney(vector<vector<int>>& ve) {
        vector<pair<int, int>> a, b;
        for (auto v : ve)
            if (v[0] < v[1]) a.push_back({v[0], v[1]});
            else b.push_back({v[0], v[1]});
        sort(begin(a), end(a), [](auto a, auto b) {
            return a.first > b.first;
        });
        sort(begin(b), end(b), [](auto a, auto b) {
            return a.second < b.second;
        });
        long long ans = 0, now = 0;
        for (auto v : b) {
            if (now < v.first) ans += v.first - now, now = v.first;
            now = now - v.first + v.second;
        }
        for (auto v : a) {
            if (now < v.first) ans += v.first - now, now = v.first;
            now = now - v.first + v.second;
        }
        return ans;
    }
};