• 双周赛117(容斥原理、记忆化搜索==>动态规划、分组背包方案数、脑经急转弯)


    双周赛117

    2928. 给小朋友们分糖果 I

    简单

    给你两个正整数 nlimit

    请你将 n 颗糖果分给 3 位小朋友,确保没有任何小朋友得到超过 limit 颗糖果,请你返回满足此条件下的 总方案数

    示例 1:

    输入:n = 5, limit = 2
    输出:3
    解释:总共有 3 种方法分配 5 颗糖果,且每位小朋友的糖果数不超过 2 :(1, 2, 2) ,(2, 1, 2) 和 (2, 2, 1) 。
    
    • 1
    • 2
    • 3

    示例 2:

    输入:n = 3, limit = 3
    输出:10
    解释:总共有 10 种方法分配 3 颗糖果,且每位小朋友的糖果数不超过 3 :(0, 0, 3) ,(0, 1, 2) ,(0, 2, 1) ,(0, 3, 0) ,(1, 0, 2) ,(1, 1, 1) ,(1, 2, 0) ,(2, 0, 1) ,(2, 1, 0) 和 (3, 0, 0) 。
    
    • 1
    • 2
    • 3

    提示:

    • 1 <= n <= 50
    • 1 <= limit <= 50

    2929. 给小朋友们分糖果 II

    中等

    给你两个正整数 nlimit

    请你将 n 颗糖果分给 3 位小朋友,确保没有任何小朋友得到超过 limit 颗糖果,请你返回满足此条件下的 总方案数

    示例 1:

    输入:n = 5, limit = 2
    输出:3
    解释:总共有 3 种方法分配 5 颗糖果,且每位小朋友的糖果数不超过 2 :(1, 2, 2) ,(2, 1, 2) 和 (2, 2, 1) 。
    
    • 1
    • 2
    • 3

    示例 2:

    输入:n = 3, limit = 3
    输出:10
    解释:总共有 10 种方法分配 3 颗糖果,且每位小朋友的糖果数不超过 3 :(0, 0, 3) ,(0, 1, 2) ,(0, 2, 1) ,(0, 3, 0) ,(1, 0, 2) ,(1, 1, 1) ,(1, 2, 0) ,(2, 0, 1) ,(2, 1, 0) 和 (3, 0, 0) 。
    
    • 1
    • 2
    • 3

    提示:

    • 1 <= n <= 106
    • 1 <= limit <= 106

    记忆化搜索 ==> 动态规划

    class Solution {
        public int distributeCandies(int n, int limit) {
            int[][] cache = new int[3][n+1];
            for(int i = 0; i < 3; i++)
                Arrays.fill(cache[i], -1);
            return dfs(2, n, limit, cache);
        }
    
        /**
        定义 dfs(i, last) 表示还要将last颗糖果分给i+1位小朋友,有多少种分法
        转移 为第i位小朋友分糖果
            dfs(i-1, last-j),其中 j <= last && j <= limit 
        递归边界 dfs(<0, 0) = 1表示分完了n颗糖果
        递归入口 dfs(2, n) 
         */
        public int dfs(int i, int last, int limit, int[][] cache){
            if(i < 0)
                return last == 0 ? 1 : 0;
            if(last < 0) return 0;
            if(cache[i][last] >= 0)  return cache[i][last];
            int res = 0;
            for(int j = 0; j <= last && j <= limit; j++){
                res += dfs(i-1, last - j, limit, cache);
            }
            return cache[i][last] = 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

    转为递推

    class Solution {
        public int distributeCandies(int n, int limit) {
            int[][] f = new int[4][n+1];
            f[0][0] = 1;
            for(int i = 0; i < 3; i++){
                for(int last = n; last >= 0; last--){
                    for(int j = 0; j <= last && j <= limit; j++){
                        f[i+1][last] += f[i][last-j];
                    }
                }
            }
            return f[3][n];
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    容斥原理

    https://leetcode.cn/problems/distribute-candies-among-children-ii/solutions/2522969/o1-rong-chi-yuan-li-pythonjavacgo-by-end-2woj/

    class Solution {
        /**
        容斥原理
        合法方案数(<= limit) = 不考虑 limit 的所有方案数 - 不合法方案数(至少有一个朋友)
    
        不考虑 limit 的所有方案数:隔板法
            有 n 个小球,放入 3 个有区分的盒子中,允许空盒的方案书
            有 n+2 个位置,选两个位置放隔板,其余n个位置放球
        ==> C(n+2, 2)
         
         不合法方案数(至少有一个朋友) 看题解
         */
        public long distributeCandies(int n, int limit) {
            return c2(n + 2) - 3 * c2(n - limit + 1) + 3 * c2(n - 2 * limit) - c2(n - 3 * limit - 1);
        }
    
        private long c2(int n) {
            return n > 1 ? (long) n * (n - 1) / 2 : 0;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    2930. 重新排列后包含指定子字符串的字符串数目

    中等

    给你一个整数 n

    如果一个字符串 s 只包含小写英文字母,s 的字符重新排列后,新字符串包含 子字符串 "leet" ,那么我们称字符串 s 是一个 字符串。

    比方说:

    • 字符串 "lteer" 是好字符串,因为重新排列后可以得到 "leetr"
    • "letl" 不是好字符串,因为无法重新排列并得到子字符串 "leet"

    请你返回长度为 n 的好字符串 数目。

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

    子字符串 是一个字符串中一段连续的字符序列。

    示例 1:

    输入:n = 4
    输出:12
    解释:总共有 12 个字符串重新排列后包含子字符串 "leet" :"eelt" ,"eetl" ,"elet" ,"elte" ,"etel" ,"etle" ,"leet" ,"lete" ,"ltee" ,"teel" ,"tele" 和 "tlee" 。
    
    • 1
    • 2
    • 3

    示例 2:

    输入:n = 10
    输出:83943898
    解释:长度为 10 的字符串重新排列后包含子字符串 "leet" 的方案数为 526083947580 。所以答案为 526083947580 % (109 + 7) = 83943898 。
    
    • 1
    • 2
    • 3

    提示:

    • 1 <= n <= 105

    记忆化搜索(分组背包方案数)

    https://leetcode.cn/problems/number-of-strings-which-can-be-rearranged-to-contain-substring/solutions/2522964/olog-n-rong-chi-yuan-li-fu-ji-yi-hua-sou-okjf/

    class Solution:
        """Java用map和String记忆化
    
        分组背包方案数
        n 组物品,每组可以选 'a' - 'z'
        至少选
            1 个 'l'
            1 个 't'
            2 个 'e'
        的方案数
    
        至少装满型背包
           背包大小:负数和0是一样的
        """
        def stringCount(self, n: int) -> int:
            return dfs(n, 1, 1, 2)
    
    @cache
    def dfs(i: int, l: int, t: int, e: int) -> int:
        if i == 0:
            return 1 if l == t == e == 0 else 0
        # 枚举选哪个
        res = dfs(i-1, 0, t, e)
        res += dfs(i-1, l, 0, e)
        res += dfs(i-1, l, t, max(e-1, 0))
        res += dfs(i-1, l, t, e) * 23 # 其余字母
        return res % (10 ** 9 + 7)
    
    • 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

    2931. 购买物品的最大开销

    困难

    给你一个下标从 0 开始大小为 m * n 的整数矩阵 values ,表示 m 个不同商店里 m * n 件不同的物品。每个商店有 n 件物品,第 i 个商店的第 j 件物品的价值为 values[i][j] 。除此以外,第 i 个商店的物品已经按照价值非递增排好序了,也就是说对于所有 0 <= j < n - 1 都有 values[i][j] >= values[i][j + 1]

    每一天,你可以在一个商店里购买一件物品。具体来说,在第 d 天,你可以:

    • 选择商店 i
    • 购买数组中最右边的物品 j ,开销为 values[i][j] * d 。换句话说,选择该商店中还没购买过的物品中最大的下标 j ,并且花费 values[i][j] * d 去购买。

    注意,所有物品都视为不同的物品。比方说如果你已经从商店 1 购买了物品 0 ,你还可以在别的商店里购买其他商店的物品 0

    请你返回购买所有 m * n 件物品需要的 最大开销

    示例 1:

    输入:values = [[8,5,2],[6,4,1],[9,7,3]]
    输出:285
    解释:第一天,从商店 1 购买物品 2 ,开销为 values[1][2] * 1 = 1 。
    第二天,从商店 0 购买物品 2 ,开销为 values[0][2] * 2 = 4 。
    第三天,从商店 2 购买物品 2 ,开销为 values[2][2] * 3 = 9 。
    第四天,从商店 1 购买物品 1 ,开销为 values[1][1] * 4 = 16 。
    第五天,从商店 0 购买物品 1 ,开销为 values[0][1] * 5 = 25 。
    第六天,从商店 1 购买物品 0 ,开销为 values[1][0] * 6 = 36 。
    第七天,从商店 2 购买物品 1 ,开销为 values[2][1] * 7 = 49 。
    第八天,从商店 0 购买物品 0 ,开销为 values[0][0] * 8 = 64 。
    第九天,从商店 2 购买物品 0 ,开销为 values[2][0] * 9 = 81 。
    所以总开销为 285 。
    285 是购买所有 m * n 件物品的最大总开销。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    示例 2:

    输入:values = [[10,8,6,4,2],[9,7,5,3,2]]
    输出:386
    解释:第一天,从商店 0 购买物品 4 ,开销为 values[0][4] * 1 = 2 。
    第二天,从商店 1 购买物品 4 ,开销为 values[1][4] * 2 = 4 。
    第三天,从商店 1 购买物品 3 ,开销为 values[1][3] * 3 = 9 。
    第四天,从商店 0 购买物品 3 ,开销为 values[0][3] * 4 = 16 。
    第五天,从商店 1 购买物品 2 ,开销为 values[1][2] * 5 = 25 。
    第六天,从商店 0 购买物品 2 ,开销为 values[0][2] * 6 = 36 。
    第七天,从商店 1 购买物品 1 ,开销为 values[1][1] * 7 = 49 。
    第八天,从商店 0 购买物品 1 ,开销为 values[0][1] * 8 = 64 。
    第九天,从商店 1 购买物品 0 ,开销为 values[1][0] * 9 = 81 。
    第十天,从商店 0 购买物品 0 ,开销为 values[0][0] * 10 = 100 。
    所以总开销为 386 。
    386 是购买所有 m * n 件物品的最大总开销。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    提示:

    • 1 <= m == values.length <= 10
    • 1 <= n == values[i].length <= 104
    • 1 <= values[i][j] <= 106
    • values[i] 按照非递增顺序排序。

    脑经急转弯

    class Solution {
        /**
        d * 越大的数 得到的结果越大
        多路归并
            每次让d乘以最小的元素
         */
        public long maxSpending(int[][] values) {
            PriorityQueue<int[]> pq = new PriorityQueue<>((a, b) -> values[a[0]][a[1]] - values[b[0]][b[1]]);
            int m = values.length, n = values[0].length;
            long res = 0;
            for(int i = 0; i < m; i++){
                pq.add(new int[]{i, n-1});
            }
            for(int d = 1; d <= m * n; d++){
                int[] q = pq.poll();
                int x = q[0], y = q[1];
                long val = values[x][y];
                if(y != 0)
                    pq.add(new int[]{x, y-1});
                res += d * val;
            }
            return 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
  • 相关阅读:
    监听和获取LayerUI中的switch值
    程序员考试下午题知识点总结
    Web 应用程序安全检查表
    【机器学习-周志华】学习笔记-第十五章
    Linux C程序编译链接的过程,gcc/g++,动态库/静态库
    Python - 函数|异常|模块|包|类和对象|封装|继承
    R语言的计量经济学实践技术应用
    UE5 官方案例LyraStarter 全特性详解 5.LyraPlayerStart
    关系的性质(自反,反自反,对称,反对称,传递)
    【医疗图像处理软件】重要功能集合
  • 原文地址:https://blog.csdn.net/qq_42958831/article/details/134474495