• 周赛366(记忆化搜索)


    周赛366

    2894. 分类求和并作差

    简单

    给你两个正整数 nm

    现定义两个整数 num1num2 ,如下所示:

    • num1:范围 [1, n] 内所有 无法被 m 整除 的整数之和。
    • num2:范围 [1, n] 内所有 能够被 m 整除 的整数之和。

    返回整数 num1 - num2

    示例 1:

    输入:n = 10, m = 3
    输出:19
    解释:在这个示例中:
    - 范围 [1, 10] 内无法被 3 整除的整数为 [1,2,4,5,7,8,10] ,num1 = 这些整数之和 = 37 。
    - 范围 [1, 10] 内能够被 3 整除的整数为 [3,6,9] ,num2 = 这些整数之和 = 18 。
    返回 37 - 18 = 19 作为答案。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    示例 2:

    输入:n = 5, m = 6
    输出:15
    解释:在这个示例中:
    - 范围 [1, 5] 内无法被 6 整除的整数为 [1,2,3,4,5] ,num1 = 这些整数之和 =  15 。
    - 范围 [1, 5] 内能够被 6 整除的整数为 [] ,num2 = 这些整数之和 = 0 。
    返回 15 - 0 = 15 作为答案。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    示例 3:

    输入:n = 5, m = 1
    输出:-15
    解释:在这个示例中:
    - 范围 [1, 5] 内无法被 1 整除的整数为 [] ,num1 = 这些整数之和 = 0 。 
    - 范围 [1, 5] 内能够被 1 整除的整数为 [1,2,3,4,5] ,num2 = 这些整数之和 = 15 。
    返回 0 - 15 = -15 作为答案。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    提示:

    • 1 <= n, m <= 1000

    模拟

    class Solution {
        public int differenceOfSums(int n, int m) {
            int num1 = 0, num2 = 0;
            for(int i = 1; i <= n; i++){
                if(i % m == 0)
                    num2 += i;
                else 
                    num1 += i;
            }
            return num1 - num2;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    2895. 最小处理时间

    中等

    你有 n 颗处理器,每颗处理器都有 4 个核心。现有 n * 4 个待执行任务,每个核心只执行 一个 任务。

    给你一个下标从 0 开始的整数数组 processorTime ,表示每颗处理器最早空闲时间。另给你一个下标从 0 开始的整数数组 tasks ,表示执行每个任务所需的时间。返回所有任务都执行完毕需要的 最小时间

    注意:每个核心独立执行任务。

    示例 1:

    输入:processorTime = [8,10], tasks = [2,2,3,1,8,7,4,5]
    输出:16
    解释:
    最优的方案是将下标为 4, 5, 6, 7 的任务分配给第一颗处理器(最早空闲时间 time = 8),下标为 0, 1, 2, 3 的任务分配给第二颗处理器(最早空闲时间 time = 10)。 
    第一颗处理器执行完所有任务需要花费的时间 = max(8 + 8, 8 + 7, 8 + 4, 8 + 5) = 16 。
    第二颗处理器执行完所有任务需要花费的时间 = max(10 + 2, 10 + 2, 10 + 3, 10 + 1) = 13 。
    因此,可以证明执行完所有任务需要花费的最小时间是 16 。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    示例 2:

    输入:processorTime = [10,20], tasks = [2,3,1,2,5,8,4,3]
    输出:23
    解释:
    最优的方案是将下标为 1, 4, 5, 6 的任务分配给第一颗处理器(最早空闲时间 time = 10),下标为 0, 2, 3, 7 的任务分配给第二颗处理器(最早空闲时间 time = 20)。 
    第一颗处理器执行完所有任务需要花费的时间 = max(10 + 3, 10 + 5, 10 + 8, 10 + 4) = 18 。 
    第二颗处理器执行完所有任务需要花费的时间 = max(20 + 2, 20 + 1, 20 + 2, 20 + 3) = 23 。 
    因此,可以证明执行完所有任务需要花费的最小时间是 23 。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    提示:

    • 1 <= n == processorTime.length <= 25000
    • 1 <= tasks.length <= 105
    • 0 <= processorTime[i] <= 109
    • 1 <= tasks[i] <= 109
    • tasks.length == 4 * n

    贪心

    class Solution {
        public int minProcessingTime(List<Integer> processorTime, List<Integer> tasks) {
            Collections.sort(tasks);
            Collections.sort(processorTime);
            int res = 0;
            int p = tasks.size()-1;
            // 让先空闲的处理器运行时间最长的任务
            for(int i = 0; i < processorTime.size(); i++){
                for(int j = 0; j < 4; j++){
                    res = Math.max(res, processorTime.get(i) + tasks.get(p--));
                }
            }
            return res;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    2896. 执行操作使两个字符串相等

    中等

    给你两个下标从 0 开始的二进制字符串 s1s2 ,两个字符串的长度都是 n ,再给你一个正整数 x

    你可以对字符串 s1 执行以下操作 任意次

    • 选择两个下标 ij ,将 s1[i]s1[j] 都反转,操作的代价为 x
    • 选择满足 i < n - 1 的下标 i ,反转 s1[i]s1[i + 1] ,操作的代价为 1

    请你返回使字符串 s1s2 相等的 最小 操作代价之和,如果无法让二者相等,返回 -1

    注意 ,反转字符的意思是将 0 变成 1 ,或者 1 变成 0

    示例 1:

    输入:s1 = "1100011000", s2 = "0101001010", x = 2
    输出:4
    解释:我们可以执行以下操作:
    - 选择 i = 3 执行第二个操作。结果字符串是 s1 = "1101111000" 。
    - 选择 i = 4 执行第二个操作。结果字符串是 s1 = "1101001000" 。
    - 选择 i = 0 和 j = 8 ,执行第一个操作。结果字符串是 s1 = "0101001010" = s2 。
    总代价是 1 + 1 + 2 = 4 。这是最小代价和。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    示例 2:

    输入:s1 = "10110", s2 = "00011", x = 4
    输出:-1
    解释:无法使两个字符串相等。
    
    • 1
    • 2
    • 3

    提示:

    • n == s1.length == s2.length
    • 1 <= n, x <= 500
    • s1s2 只包含字符 '0''1'

    记忆化搜索(O(n^2)DP)

    https://leetcode.cn/problems/apply-operations-to-make-two-strings-equal/solutions/2472122/ji-yi-hua-sou-suo-by-endlesscheng-64vq/

    class Solution {
        /**
        什么时候返回-1 ? 当s1和s2的奇偶性不同时,无法通过反转变成相同
        
        如果相同,那么无需修改
        如果不同,
            选择第一种操作,相当于后面可以免费反转一个字符
            选择第二种操作,那么下一个字符要把0看作1,1看作0
    
         */
        char[] s, t;
        int x;
        int[][][] cache;
        public int minOperations(String s1, String s2, int x) {
            s = s1.toCharArray();
            t = s2.toCharArray();
            this.x = x;
            int n = s.length, diff = 0;
            for(int i = 0; i < n; i++){
                diff ^= (s[i] ^ t[i]);
            }
            if(diff != 0)
                return -1;
            cache = new int[n][n+1][2];
            for(int i = 0; i < n; i++)
                for(int j = 0; j <= n; j++)
                    Arrays.fill(cache[i][j], -1);
            return dfs(n-1, 0, 0);
        }
        /**
            定义 dfs(i, j, pre_rev) 表示翻转s1[:i],免费反转次数j次,上一个字符是否选择了第二种操作
            如果 (s1[i] == s[1]) == (! pre_rev) 表示 s1[i] 和 s2[i] 是相等的,无需操作,返回 dfs(i-1, j, false) 
            否则
                选择第一种操作 dfs(i-1, j+1, false) + x
                选择第二种操作 dfs(i-1, j, true) + 1
                如果 j > 0, 免费翻转一次 dfs(i-1, j-1, false)
            取最小值
         */
        public int dfs(int i, int j, int pre_rev){
            if(i < 0){
                if(j > 0 || pre_rev > 0)
                    return Integer.MAX_VALUE / 2;
                return 0;
            }
            if(cache[i][j][pre_rev] != -1) return cache[i][j][pre_rev];
            // if(pre_rev == 0){
            //     if(s[i] == t[i]){ // 不需要反转
            //         return dfs(i-1, j, 0);
            //     }
            // }
            if((s[i] == t[i]) == (pre_rev == 0))
                return dfs(i-1, j, 0);
            int res = Math.min(dfs(i-1, j+1, 0) + x, dfs(i-1, j, 1) + 1);
            if(j > 0){ // 可以免费反转
                res = Math.min(res, dfs(i-1, j-1, 0));
            }
            return cache[i][j][pre_rev] = res;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59

    记忆化搜索(O(n)DP)

    class Solution {
        /**
        统计下标不同的位置到数组p中
        不合法的情况,p 的长度是奇数
    
        定义 f[i] 表示修改 p 的前 i 个位置的最小花费
    
        第一种操作 dfs(i-1) + x / 2
        第二种操作 dfs(i-2) + p[i] - p[i-1] 
         */
        public int minOperations(String s1, String s2, int 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 newF = Math.min(f1 + x, f0 + (p.get(i) - p.get(i - 1)) * 2);
                f0 = f1;
                f1 = newF;
            }
            return f1 / 2;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    2897. 对数组执行操作使平方和最大

    困难

    给你一个下标从 0 开始的整数数组 nums 和一个 整数 k

    你可以对数组执行以下操作 任意次

    • 选择两个互不相同的下标 ij同时nums[i] 更新为 (nums[i] AND nums[j]) 且将 nums[j] 更新为 (nums[i] OR nums[j])OR 表示按位 运算,AND 表示按位 运算。

    你需要从最终的数组里选择 k 个元素,并计算它们的 平方 之和。

    请你返回你可以得到的 最大 平方和。

    由于答案可能会很大,将答案对 109 + 7 取余 后返回。

    示例 1:

    输入:nums = [2,6,5,8], k = 2
    输出:261
    解释:我们可以对数组执行以下操作:
    - 选择 i = 0 和 j = 3 ,同时将 nums[0] 变为 (2 AND 8) = 0 且 nums[3] 变为 (2 OR 8) = 10 ,结果数组为 nums = [0,6,5,10] 。
    - 选择 i = 2 和 j = 3 ,同时将 nums[2] 变为 (5 AND 10) = 0 且 nums[3] 变为 (5 OR 10) = 15 ,结果数组为 nums = [0,6,0,15] 。
    从最终数组里选择元素 15 和 6 ,平方和为 152 + 62 = 261 。
    261 是可以得到的最大结果。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    示例 2:

    输入:nums = [4,5,4,7], k = 3
    输出:90
    解释:不需要执行任何操作。
    选择元素 7 ,5 和 4 ,平方和为 72 + 52 + 42 = 90 。
    90 是可以得到的最大结果。
    
    • 1
    • 2
    • 3
    • 4
    • 5

    提示:

    • 1 <= k <= nums.length <= 105
    • 1 <= nums[i] <= 109
  • 相关阅读:
    背诵考研英语单词计划总览
    今日ac题
    【无标题】
    无胁科技-TVD每日漏洞情报-2022-10-31
    字符串的创建及常用方法大全
    下载vscode 更新
    「Python循环结构」使用for循环实现高斯求和、阶乘、斐波那契数列
    线程的创建方式4:使用线程池
    现在做跨境电商还需要全球代理IP吗?全球代理IP哪家靠谱?
    第四章:指令系统
  • 原文地址:https://blog.csdn.net/qq_42958831/article/details/133842185