周赛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 作为答案。

示例 2:

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

示例 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 <= 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;
    }
}

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 。

示例 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 <= 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;
    }
}

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 。这是最小代价和。

示例 2:

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

提示:

  • 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;
    }
}

记忆化搜索(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;
    }
}

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 是可以得到的最大结果。

示例 2:

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

提示:

  • 1 <= k <= nums.length <= 105
  • 1 <= nums[i] <= 109

你可能感兴趣的:(算法刷题记录,#,LC周赛,深度优先,算法)