LeetCode第366场周赛学习笔记(学习自灵茶山艾府 AKA 灵神)

本次学习笔记将分析LeetCode周赛第365场的题目,并提供相关的解题思路和代码实现。这场周赛包括四道题目,分别讨论每道题的解法。

声明:

这篇学习笔记是在学习了灵神对于这次周赛的解答然后整理出来的,大家如果不满意可以去看灵神写的解析:

题解+多种语言代码:
Q1 https://leetcode.cn/problems/divisible-and-non-divisible-sums-difference/solution/o1-shu-xue-gong-shi-yi-xing-gao-ding-pyt-m5cq/
Q2 https://leetcode.cn/problems/minimum-processing-time/solution/tan-xin-pythonjavacgo-by-endlesscheng-8fzf/
Q3 https://leetcode.cn/problems/apply-operations-to-make-two-strings-equal/solution/ji-yi-hua-sou-suo-by-endlesscheng-64vq/
Q4 https://leetcode.cn/problems/apply-operations-on-array-to-maximize-sum-of-squares/solution/ba-1-du-ju-zai-yi-qi-pythonjavacgo-by-en-rzng/

第一题:2894. 分类求和并作差 - 力扣(LeetCode)

class Solution {
    public int differenceOfSums(int n, int m) {
        //灵神版本:1,2,...,n个数之和 - 2 *(m,2m,...,n/m的下取整*m个数之和) return (n*(n+1)/2)-(n/m*(n/m+1)*m);
        //我的是暴力搜索(菜狗做法)因为只有1000个数所以可以枚举。
        int count=0;
        for(int i=1;i<=n;i++){
            if(i%m==0){
                count-=i;
            }else count+=i;
        }
        return count;
    }
}

 第二题:2895. 最小处理时间 - 力扣(LeetCode)

class Solution {
    public int minProcessingTime(List<Integer> processorTime, List<Integer> tasks) {
        //灵神版本:把 processorTime\textit{processorTime}processorTime 从小到大排序,
//tasks\textit{tasks}tasks 从大到小排序,那么答案就是processorTime[i]+tasks[4i]的最大值。
        Collections.sort(processorTime);
        tasks.sort(Collections.reverseOrder());
        int ans = 0;
        for (int i = 0; i < processorTime.size(); i++) {
            ans = Math.max(ans, processorTime.get(i) + tasks.get(i * 4));
        }
        return ans;

    }
}
/*我的也是贪心算法,先排序,然后空闲时间小的对应处理时间长的,浪费了很多空间,做了不少的无用功,但是大致意思差不多的。
Collections.sort(tasks);
        Collections.sort(processorTime);
        int n=processorTime.size();
        //for(int i=0;i<4*n;i++){System.out.println(tasks.get(i));}
        
        int[] ass=new int[n];
        int tmp=0;
        for(int i=0;i<n;i++){
            ass[i]=Math.max(tasks.get(tmp+3),ass[i]);
            //System.out.println(ass[i]);
            tmp=tmp+4;
        }
        int max=0,j=0;
        for(int i=n-1;i>=0;i--){
            int f=processorTime.get(i);
            if(max<f+ass[j]) max=f+ass[j];
            //System.out.println(ass[j]);
            j++;
        }
        return max;
 */

 第三题:2896. 执行操作使两个字符串相等 - 力扣(LeetCode)

class Solution {
    public int minOperations(String s1, String s2, int x) {
        /*
        贪心算法在此处是有错误的,大家看到这道题直觉会使用
        例子:1000110001(两个二进制数按位异或)
        当两个对应值不同的下标差 < = x 时,可以使用操作二来反转得到
        0000010001——>代价为3,再来一次,得到0000000000,总代价为
        6=3+3;
        而如果一开始就对中间的两个1进行操作二,再对处在两边的1进行操作一,总代价仅为3+1=4,因此本题应使用其他方法——dp。
        这里有一个小技巧:当看到题目给的数的范围比较小的最优问题一般选择动态规划——dp。
        dp本质上就是优化后的暴力算法,不要害怕它。
        dp怎么求呢?找子问题(推荐从左到右思考)
        如果使用操作一的话,那当前代价加上x,但同时也带来一次没有代价的反转,故需要增加一个变量 j 来记录免费反转次数。
        如果使用操作二的话,那根据对应下标差计算,但同时设置一个数表示有没有进行过操作二以避免相邻的数没有不合法;
        同时递归边界为当数组遍历完毕后,仍然有免费转换次数或者仍然有相邻的数未反转则视为不合法,返回一个无穷大。
        上述思想也可以转为对两个二进制数 异或 后的结果进行操作=>转化为递推。
         */
        /*灵神版本1:递归dp(记忆化搜索)
        char[] s=s1.toCharArray(), t=s2.toCharArray();
        int n=s.length,diff=0;
        for(int i=0;i<n;i++){
            if(s[i]!=t[i]){
                diff^=1;
            }
        }
        if(diff!=0) return -1;
        int[][][] memo=new int[n][n+1][2];
        for(int i=0;i<n;i++){
            for(int j=0;j<=n;j++){
                Arrays.fill(memo[i][j],-1);
            }
        } 
        return dfs(n-1,0,0,memo,s,t,x);*/

        //灵神版本二:对两个二进制数异或后的结果进行操作。
        if (s1.equals(s2)) {
            return 0;
        }
        List<Integer> p = new ArrayList<>();
        for (int i = 0; i < s1.length(); i++) {
            if (s1.charAt(i) != s2.charAt(i)) {
                p.add(i);
            }
        }
        if (p.size() % 2 != 0) {
            return -1;
        }

        int f0=0,f1=x;
        for(int i=1;i<p.size();i++){
            int new_f=Math.min(f1+x,f0+(p.get(i)-p.get(i-1))*2);
            f0=f1;
            f1=new_f;
        }
        return f1/2;
    }
    /*private int dfs(int i,int j,int preRev, int[][][] memo,char[] s, char[] t, int x){
        if(i<0){
            return j>0||preRev>0? Integer.MAX_VALUE/2:0;
        }
        if(memo[i][j][preRev]!=-1){
            return memo[i][j][preRev];
        }
        if((s[i]==t[i])==(preRev==0)){
            return dfs(i-1,j,0,memo,s,t,x);
        }
        int res=Math.min(dfs(i-1,j+1,0,memo,s,t,x)+x,dfs(i-1,j,1,memo,s,t,x)+1);
        if(j>0){
            res=Math.min(res,dfs(i-1,j-1,0,memo,s,t,x));
        }
        return memo[i][j][preRev]=res;
    }*/
}

 第四题:2897. 对数组执行操作使平方和最大 - 力扣(LeetCode)

class Solution {
    /**
    位运算考虑三个方向:
    1.能不能按位做
    2.XOR AND OR (
        仔细分析它们的性质:
        XOR可类比到加法(模二的加法)
        AND 运算的数越多越小
        OR 运算的数越多越大
    )
    3.字典树tries(只适用于 XOR )
    
    由题目可以推断:
    当 x < y 时,而且 x 有个比特位是1,y 有个比特位是0,
    那么可以将 x 的 1 移到 y 的 0 上
    要想结果更大,则应该尽量操作
    这里也可以使用集合论的思想去思考,0看做是一个空位,1看做是一个值

    交换前比交换后的值小 :(y+d)的平方+(x-d)的平方 > x的平方+y的平方:(因为y>x => 2yd-2xd+2*d的平方 > 0)
    
    故步骤如下:
    1.统计每一位有多少个 1
    2.每次构造/组合一个数的时候,使用尽可能多的 1
    有关位运算的技巧,请看 https://leetcode.cn/circle/discuss/CaOJ45/
     */
    public int maxSum(List<Integer> nums, int k) {
        final long MOD=1_000_000_007;
        /*1 <= nums[i] <= 10的9次方,那它的二进制形式总不能超过
        2的30次方吧。
        */
        int[] cnt=new int[30];
        for(int x:nums){
            for(int i=0;i<30;i++){
                cnt[i]+=( x>>i ) & 1;
            }
        }
        long ans=0;
        while(k-->0){
            int x=0;
            for(int i=0;i<30;i++){
                if(cnt[i]>0){
                    cnt[i]--;
                    x|= 1<<i ;
                }
            }
            ans=(ans + (long) x*x ) % MOD;
        }
        return (int) ans;
    }
}

 以上就是对LeetCode周赛第366场题目的解析和分析。希望这些解题思路能够帮助你更好地理解和解决类似的问题。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值