Leetcode - 127双周赛

目录

一,3095. 或值至少 K 的最短子数组 I

二,3096. 得到更多分数的最少关卡数目

三,3097. 或值至少为 K 的最短子数组 II

四,3098. 求出所有子序列的能量和


一,3095. 或值至少 K 的最短子数组 I

本题需要知道一个知识点,0|0 = 0,0|1 = 1,1|1 = 1,根据上述性质可以得出,每按位或一个数,这个数要么变大,要么不变,也就是说它有一个非递减的性质。在者,这道题的数据范围不大,可以直接暴力,代码如下: 

class Solution {
    public int minimumSubarrayLength(int[] nums, int k) {
        int n = nums.length;
        int ans = Integer.MAX_VALUE;
        for(int i=0; i<n; i++){
            int or = 0;
            for(int j=i; j<n; j++){
                or |= nums[j];
                if(or >= k){
                    ans = Math.min(ans, j-i+1);
                    break;
                }
            }
        }
        return ans==Integer.MAX_VALUE?-1:ans;
    }
}

二,3096. 得到更多分数的最少关卡数目

本题实际上就是求前缀和大于后缀和的所需要的最小数量,需要先统计整个数组的和sum,再遍历数组possible,用一个额外变量pre统计它的前缀和,那么sum - pre就是它的后缀和,如果 pre > sum - pre,返回结果,代码如下:

class Solution {
    public int minimumLevels(int[] possible) {
        int sum = 0;
        for(int x : possible) 
            sum += x==0?-1:1;
        int n = possible.length;
        int pre = 0;
        for(int i=0; i<n; i++){
            if(i>0 && pre > sum - pre)
                return i;
            pre += possible[i]==0?-1:1;
        }
        return -1;
    }
}

三,3097. 或值至少为 K 的最短子数组 II

本题和第一题相同,但是数据范围更大,无法使用暴力,但是结论可以使用:一个数按位或的越多,那么就会变大或不变

方法一:滑动窗口

使用一个大小为32的数组来统计32个比特位各出现了几次

  • 每遍历到一个数,遍历它的32个bit位,如果cnt[i]==0 && (x>>i)&1==1时,说明当前数字按位或后,这个bit位会从0变成1,所以 k -= 1<<i
  • 当 k <= 0 时,说明当前按位与的数已经大于k了,就可以更新ans,同时可以缩减[l,r]的范围,看看当l变大时,是否满足k<=0,注意删除nums[l]时,遍历它的32个bit位,如果cnt[i]==1 && ((nums[l]>>i)&1)==1,说明删除这个数后,按位或的这个数会减小,所以 k += 1<<i
  • 注意当 k == 0 时,直接返回 1
class Solution {
    public int minimumSubarrayLength(int[] nums, int k) {
        int[] cnt = new int[32];//统计bit位出现了几次
        int n = nums.length;
        int ans = Integer.MAX_VALUE;
        if(k == 0) return 1;
        for(int l=0,r=0; r<n; r++){
            int x = nums[r];
            for(int i=0; i<32; i++){
                if(cnt[i]==0 && ((x>>i)&1)==1)//按位或之后,这个bit位会从0变成1
                    k -= 1<<i;
                cnt[i] += (x>>i)&1;
            }
            while(k <= 0){
                ans = Math.min(ans, r-l+1);//跟新答案
                int y = nums[l];
                for(int i=0; i<32; i++){
                    if(cnt[i]==1 && ((y>>i)&1)==1)//丢掉y之后,这个bit位是否会从1变成0
                        k += 1<<i;
                    cnt[i] -= (y>>i)&1;
                }
                l++;
            }
        }
        return ans==Integer.MAX_VALUE?-1:ans;
    }
}

方法二:通用模板

遍历数组nums,使用二维数组不停的更新以 i 为右端点的按位或值,及其最大左端点,同时计算符合条件的最短子数组

class Solution {
    public int minimumSubarrayLength(int[] nums, int k) {
        int ans = Integer.MAX_VALUE;
        int[][] or = new int[32][2];
        int m = 0;
        int n = nums.length;
        for(int i=0; i<n; i++){
            or[m][0] = 0;
            or[m++][1] = i;
            //更新操作
            int j = 0;
            for(int idx=0; idx < m; idx++){
                or[idx][0] |= nums[i];
                if(or[idx][0] >= k){
                    ans = Math.min(ans, i-or[idx][1]+1);
                }
                if(or[idx][0] != or[j][0])//去重
                    or[++j][0] = or[idx][0];
                or[j][1] = or[idx][1];
            }
            m = j+1;
        }
        return ans == Integer.MAX_VALUE ? -1 : ans;
    }
}

四,3098. 求出所有子序列的能量和

本题是一道单纯的dfs+记忆化题,有两种做法:

  • 枚举选哪个
  • 选或不选

枚举选哪个:

class Solution {
    static final int MOD = (int)1e9+7;
    int[] nums;
    int n;
    Map<Long, Long> map = new HashMap<>();
    public int sumOfPowers(int[] nums, int k) {
        this.nums = nums;
        Arrays.sort(nums);
        this.n = nums.length;
        long ans = dfs(0,k,Integer.MAX_VALUE, -1);
        return (int)ans%MOD;
    }
    long dfs(int idx, int k, int min, int j){
        if(k == 0){
            return min;
        }
        long res = 0;
        long key = ((long)min<<18|idx<<12|k<<6|(j+1));
        if(map.containsKey(key)) return map.get(key);
        for(int i=idx; i<=n-k; i++){
            res = (res + dfs(i+1, k-1, Math.min(min, (j==-1?Integer.MAX_VALUE:Math.abs(nums[i]-nums[j]))), i))%MOD;
        }
        map.put(key, res);
        return res;
    }
}

 选或不选:

class Solution {
    static final int MOD = (int)1e9+7;
    int[] nums;
    int n;
    Map<Long, Long> map = new HashMap<>();
    public int sumOfPowers(int[] nums, int k) {
        this.nums = nums;
        Arrays.sort(nums);
        this.n = nums.length;
        long ans = dfs(-1,k,Integer.MAX_VALUE, -1);
        return (int)ans%MOD;
    }
    //选或不选
    long dfs(int i, int k, int min, int j){
        if(k == 0)
            return min;
        if(k > n - i - 1)
            return 0;
        long res = 0;
        long key = ((long)min<<18|i<<12|k<<6|(j+1));
        if(map.containsKey(key)) return map.get(key);
        //选nums[i+1]
        res = dfs(i+1, k-1, Math.min(min, (j==-1?Integer.MAX_VALUE:Math.abs(nums[i+1]-nums[j]))), i+1);
        //不选nums[i+1]
        res += dfs(i+1, k, min, j);
        map.put(key, res%MOD);
        return res%MOD;
    }
}

  • 12
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一叶祇秋

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值