【LeetCode周赛】第371场周赛

100120. 找出强数对的最大异或值 I 简单

给你一个下标从 0 开始的整数数组 nums 。如果一对整数 x 和 y 满足以下条件,则称其为 强数对 : ∣ x − y ∣ < = m i n ( x , y ) |x - y| <= min(x, y) xy<=min(x,y)
你需要从 nums 中选出两个整数,且满足:这两个整数可以形成一个强数对,并且它们的按位异或(XOR)值是在该数组所有强数对中的 最大值 。

返回数组 nums 所有可能的强数对中的 最大 异或值。

注意,你可以选择同一个整数两次来形成一个强数对。

示例 1:

输入:nums = [1,2,3,4,5]
输出:7
解释:数组 nums 中有 11 个强数对:(1, 1), (1, 2), (2, 2), (2, 3), (2, 4), (3, 3), (3, 4), (3, 5), (4, 4), (4, 5) 和 (5, 5) 。
这些强数对中的最大异或值是 3 XOR 4 = 7 。

示例 2

输入:nums = [10,100]
输出:0
解释:数组 nums 中有 2 个强数对:(10, 10) 和 (100, 100) 。
这些强数对中的最大异或值是 10 XOR 10 = 0 ,数对 (100, 100) 的异或值也是 100 XOR 100 = 0 。

示例 3:

输入:nums = [5,6,25,30]
输出:7
解释:数组 nums 中有 6 个强数对:(5, 5), (5, 6), (6, 6), (25, 25), (25, 30) 和 (30, 30) 。
这些强数对中的最大异或值是 25 XOR 30 = 7 ;另一个异或值非零的数对是 (5, 6) ,其异或值是 5 XOR 6 = 3 。

提示:

  • 1 < = n u m s . l e n g t h < = 50 1 <= nums.length <= 50 1<=nums.length<=50
  • 1 < = n u m s [ i ] < = 100 1 <= nums[i] <= 100 1<=nums[i]<=100

分析:
按照题意枚举强数对,不断更新最大异或值。

代码:

class Solution {
public:
    int maximumStrongPairXor(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        int n=nums.size(), ans=0;
        for(int i=0;i<n;i++){
            for(int j=i;j<n;j++){
                if(nums[j]-nums[i]<=nums[i])
                    ans=max(ans, nums[i]^nums[j]);
            }
        }
        return ans;
    }
};


2933. 高访问员工 中等

给你一个长度为 n 、下标从 0 开始的二维字符串数组 access_times 。对于每个 i(0 <= i <= n - 1 ),access_times[i][0] 表示某位员工的姓名,access_times[i][1] 表示该员工的访问时间。access_times 中的所有条目都发生在同一天内

访问时间用 四位 数字表示, 符合 24 小时制 ,例如 “0800” 或 “2250” 。

如果员工在 同一小时内 访问系统 三次或更多 ,则称其为 高访问 员工。

时间间隔正好相差一小时的时间 被视为同一小时内。例如,“0815” 和 “0915” 不属于同一小时内。

一天开始和结束时的访问时间不被计算为同一小时内。例如,“0005” 和 “2350” 不属于同一小时内。

以列表形式,按任意顺序,返回所有 高访问 员工的姓名。

示例 1:

输入:access_times = [[“a”,“0549”],[“b”,“0457”],[“a”,“0532”],[“a”,“0621”],[“b”,“0540”]]
输出:[“a”]
解释:“a” 在时间段 [05:32, 06:31] 内有三条访问记录,时间分别为 05:32 、05:49 和 06:21 。
但是 “b” 的访问记录只有两条。
因此,答案是 [“a”] 。

示例 2:

输入:access_times = [[“d”,“0002”],[“c”,“0808”],[“c”,“0829”],[“e”,“0215”],[“d”,“1508”],[“d”,“1444”],[“d”,“1410”],[“c”,“0809”]]
输出:[“c”,“d”]
解释:“c” 在时间段 [08:08, 09:07] 内有三条访问记录,时间分别为 08:08 、08:09 和 08:29 。
“d” 在时间段 [14:10, 15:09] 内有三条访问记录,时间分别为 14:10 、14:44 和 15:08 。
然而,“e” 只有一条访问记录,因此不能包含在答案中,最终答案是 [“c”,“d”] 。

示例 3:

输入:access_times = [[“cd”,“1025”],[“ab”,“1025”],[“cd”,“1046”],[“cd”,“1055”],[“ab”,“1124”],[“ab”,“1120”]]
输出:[“ab”,“cd”]
解释:"ab"在时间段 [10:25, 11:24] 内有三条访问记录,时间分别为 10:25 、11:20 和 11:24 。
“cd” 在时间段 [10:25, 11:24] 内有三条访问记录,时间分别为 10:25 、10:46 和 10:55 。
因此,答案是 [“ab”,“cd”] 。

提示:

  • 1 <= access_times.length <= 100
  • access_times[i].length == 2
  • 1 <= access_times[i][0].length <= 10
  • access_times[i][0] 仅由 小写英文字母 组成。
  • access_times[i][1].length == 4
  • access_times[i][1] 采用24小时制表示时间。
  • access_times[i][1] 仅由数字 ‘0’ 到 ‘9’ 组成。

分析:
记录每一个人的所有访问时间。
高访问者:同一个小时内次数有三次或者更高。
将每个人的访问时间按照顺序进行排序,找到连续三个访问时间在同一小时的,则记录该访问者。

代码:

class Solution {
public:
    vector<string> findHighAccessEmployees(vector<vector<string>>& access_times) {
        unordered_map<string, vector<int>> m;
        vector<string> ans;
        for(auto& at : access_times){
            int time = ((at[1][0]-'0')*10 + at[1][1]-'0')*60 + ((at[1][2]-'0')*10 + at[1][3]-'0');
            m[at[0]].push_back(time);
        }
        for(auto [k, v] : m){
            if(v.size()<3) continue;
            sort(v.begin(), v.end());
            int n=v.size();
            for(int i=0;i<n-2;i++){
                if(v[i+2]-v[i]<60){
                    ans.push_back(k);
                    break;
                }
            }
        }
        return ans;
    }
};


100117. 最大化数组末位元素的最少操作次数 中等

给你两个下标从 0 开始的整数数组 nums1 和 nums2 ,这两个数组的长度都是 n 。

你可以执行一系列 操作(可能不执行)。

在每次操作中,你可以选择一个在范围 [0, n - 1] 内的下标 i ,并交换 nums1[i] 和 nums2[i] 的值。

你的任务是找到满足以下条件所需的 最小 操作次数:

nums1[n - 1] 等于 nums1 中所有元素的 最大值 ,即 nums1[n - 1] = max(nums1[0], nums1[1], …, nums1[n - 1]) 。
nums2[n - 1] 等于 nums2 中所有元素的 最大值 ,即 nums2[n - 1] = max(nums2[0], nums2[1], …, nums2[n - 1]) 。
以整数形式,表示并返回满足上述 全部 条件所需的 最小 操作次数,如果无法同时满足两个条件,则返回 -1 。

示例 1:

输入:nums1 = [1,2,7],nums2 = [4,5,3]
输出:1
解释:在这个示例中,可以选择下标 i = 2 执行一次操作。
交换 nums1[2] 和 nums2[2] 的值,nums1 变为 [1,2,3] ,nums2 变为 [4,5,7] 。
同时满足两个条件。
可以证明,需要执行的最小操作次数为 1 。
因此,答案是 1 。

示例 2:

输入:nums1 = [2,3,4,5,9],nums2 = [8,8,4,4,4]
输出:2
解释:在这个示例中,可以执行以下操作:
首先,选择下标 i = 4 执行操作。
交换 nums1[4] 和 nums2[4] 的值,nums1 变为 [2,3,4,5,4] ,nums2 变为 [8,8,4,4,9] 。
然后,选择下标 i = 3 执行操作。
交换 nums1[3] 和 nums2[3] 的值,nums1 变为 [2,3,4,4,4] ,nums2 变为 [8,8,4,5,9] 。
同时满足两个条件。
可以证明,需要执行的最小操作次数为 2 。
因此,答案是 2 。

示例 3:

输入:nums1 = [1,5,4],nums2 = [2,5,3]
输出:-1
解释:在这个示例中,无法同时满足两个条件。
因此,答案是 -1 。

提示:

  • 1 < = n = = n u m s 1. l e n g t h = = n u m s 2. l e n g t h < = 1000 1 <= n == nums1.length == nums2.length <= 1000 1<=n==nums1.length==nums2.length<=1000
  • 1 < = n u m s 1 [ i ] < = 1 0 9 1 <= nums1[i] <= 10^9 1<=nums1[i]<=109
  • 1 < = n u m s 2 [ i ] < = 1 0 9 1 <= nums2[i] <= 10^9 1<=nums2[i]<=109

分析:

只有两种情况:nums1[n-1]和nums2[n-1]不交换、nums1[n-1]和nums2[n-1]交换
对这两种情况分别判断就好了。
如果这两种情况中,nums1[1…n-2]和nums2[1…n-2]不管怎么交换都不能满足情况,则不能满足返回-1.

代码:

class Solution {
public:
    int minOperations(vector<int>& nums1, vector<int>& nums2) {
        int n=nums1.size();
        int cnt1=0,cnt2=1;
        for(int i=0;i<n-1;i++){
            if(nums1[n-1]<nums1[i]||nums2[n-1]<nums2[i]){
                if(nums1[n-1]<nums2[i]||nums2[n-1]<nums1[i]) {
                    cnt1=-1;
                    break;
                }
                cnt1++;
            }
        }
        swap(nums1[n-1], nums2[n-1]);
        for(int i=0;i<n-1;i++){
            if(nums1[n-1]<nums1[i]||nums2[n-1]<nums2[i]){
                if(nums1[n-1]<nums2[i]||nums2[n-1]<nums1[i]) {
                    cnt2=-1;
                    break;
                }
                cnt2++;
            }
        }
        if(cnt1==-1&&cnt2==-1) return -1;
        return min(cnt1, cnt2);
    }
};


2935. 找出强数对的最大异或值 II 困难

给你一个下标从 0 开始的整数数组 nums 。如果一对整数 x 和 y 满足以下条件,则称其为 强数对 :
∣ x − y ∣ < = m i n ( x , y ) |x - y| <= min(x, y) xy<=min(x,y)
你需要从 nums 中选出两个整数,且满足:这两个整数可以形成一个强数对,并且它们的按位异或(XOR)值是在该数组所有强数对中的 最大值 。

返回数组 nums 所有可能的强数对中的 最大 异或值。

注意,你可以选择同一个整数两次来形成一个强数对。

示例 1:

输入:nums = [1,2,3,4,5]
输出:7
解释:数组 nums 中有 11 个强数对:(1, 1), (1, 2), (2, 2), (2, 3), (2, 4), (3, 3), (3, 4), (3, 5), (4, 4), (4, 5) 和 (5, 5) 。
这些强数对中的最大异或值是 3 XOR 4 = 7 。

示例 2:

输入:nums = [10,100]
输出:0
解释:数组 nums 中有 2 个强数对:(10, 10) 和 (100, 100) 。
这些强数对中的最大异或值是 10 XOR 10 = 0 ,数对 (100, 100) 的异或值也是 100 XOR 100 = 0 。

示例 3:

输入:nums = [500,520,2500,3000]
输出:1020
解释:数组 nums 中有 6 个强数对:(500, 500), (500, 520), (520, 520), (2500, 2500), (2500, 3000) 和 (3000, 3000) 。
这些强数对中的最大异或值是 500 XOR 520 = 1020 ;另一个异或值非零的数对是 (5, 6) ,其异或值是 2500 XOR 3000 = 636 。

提示:

  • 1 < = n u m s . l e n g t h < = 5 ∗ 1 0 4 1 <= nums.length <= 5 * 10^4 1<=nums.length<=5104
  • 1 < = n u m s [ i ] < = 2 20 − 1 1 <= nums[i] <= 2^{20} - 1 1<=nums[i]<=2201

分析:
根据题解得到的分析:
从数据出发,需要不断地枚举强数对,再不断更新异或值。无疑这种方法时间复杂度很高。

可以从结果出发,最终的结果一定是一个整形,且根据题目给的范围,nums[i] 转换成二进制后,最大也就 2 20 − 1 2^{20}-1 2201,因此,我们从异或能得到的最高位开始枚举,判断他能否为1。
判断的过程需要遍历一次升序的nums[0…n-1],并且使用map记录出现过的值。

nums = [1, 2, 3, 4, 5],将其转换成二进制,如图。
经过观察,可以判断nums中两个数异或,最多只能让二进制的第三位为1,因此前面可以跳过。
先判断第三位能不能有强数对使其为1。可以得到在 011 和 100这一强数对可以满足,保存ans为 100 。

在这里插入图片描述
接下来判断同时能使第三位和第二位能不能同时为1。
可以看到 011 和 100 这一强数对可以满足,保存ans为 110 。

在这里插入图片描述
接下来判断,在第三位和第二位都能为1的情况下,能不能使第一位也为1。
最终可以看到 011 和 100 这一强数对可以满足,保存ans为 111 。

在这里插入图片描述

在这里插入图片描述
计算结束,因此可以得到满足条件的最大异或值为 7 。

代码:

class Solution {
public:
    int maximumStrongPairXor(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        int n=nums.size(), ans=0, mask=0;
        unordered_map<int, int> mp;
        for(int i=31;i>=0;i--){
            mask |= (1<<i);
            mp.clear(); // 清楚mp,用于下一次异或值的判断
            int n_ans = ans | (1<<i);
            for(int num : nums){
                int mask_y = num & mask; // 只需要判断高的那些位数,低位置0
                auto it = mp.find(n_ans^mask_y); // 依照异或的交换律,从mp中寻找能使高为为1的值。
                if(it!=mp.end() && it->second * 2 >= num){// 数组已排序,先加入mp的为更小的。在此判断是否有该值,同时也判断是否满足 强数对 的要求
                    ans = n_ans; // 能满足的话,说明有更大的异或值。
                    break;// 只要存在满足的,后续就不需要再继续判断了。
                }
                mp[mask_y]=num;// 如果不满足,则记录当前值,便于后续判断。
            }
        }
        return ans;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值