• [Leetcode Weekly Contest]301


    链接:LeetCode

    [Leetcode]2335. 装满杯子需要的最短总时长

    现有一台饮水机,可以制备冷水、温水和热水。每秒钟,可以装满 2 杯 不同 类型的水或者 1 杯任意类型的水。
    给你一个下标从 0 开始、长度为 3 的整数数组 amount ,其中 amount[0]、amount[1] 和 amount[2] 分别表示需要装满冷水、温水和热水的杯子数量。返回装满所有杯子所需的 最少 秒数。

    简单贪心。
    肯定是最好每次都选两个。分两种情况,一种是有一种水特别多,那么答案就是这种水的数量。否则,一定可以匹配到只剩一杯,或匹配完。

    class Solution {
        public int fillCups(int[] amount) {
            Arrays.sort(amount);
            if(amount[2] == 0) return 0;
            if(amount[2] > amount[0] + amount[1]) return amount[2];
            else return 1+fillCups(new int[]{amount[0], amount[1]-1, amount[2]-1});
        }
    }
    

    [Leetcode]2336. 无限集中的最小数字

    现有一个包含所有正整数的集合 [1, 2, 3, 4, 5, ...] 。
    实现 SmallestInfiniteSet 类:

    • SmallestInfiniteSet() 初始化 SmallestInfiniteSet 对象以包含 所有 正整数。
    • int popSmallest() 移除 并返回该无限集中的最小整数。
    • void addBack(int num) 如果正整数 num 不 存在于无限集中,则将一个 num 添加 到该无限集中。

    逆向思维,既然原来的集合是无限的,那我就构造一个有限的。 收集被无限集踢掉的元素、踢掉被无限集收录的元素。

    class SmallestInfiniteSet {
        HashSet<Integer> removeSet = new HashSet<>();
        int cur = 1;
    
        public SmallestInfiniteSet() {
        }
    
        public int popSmallest() {
            int res = cur;
            while(removeSet.contains(res)) {
                res++;
            }
            removeSet.add(res);
            cur = res+1;
            return res;
        }
    
        public void addBack(int num) {
            cur = Math.min(cur, num);
            if(removeSet.contains(num)) removeSet.remove(num);
        }
    }
    
    /**
     * Your SmallestInfiniteSet object will be instantiated and called as such:
     * SmallestInfiniteSet obj = new SmallestInfiniteSet();
     * int param_1 = obj.popSmallest();
     * obj.addBack(num);
     */
    

    [Leetcode]2337. 移动片段得到字符串

    给你两个字符串 start 和 target ,长度均为 n 。每个字符串 仅 由字符 'L'、'R' 和 '_' 组成,其中:

    • 字符 'L' 和 'R' 表示片段,其中片段 'L' 只有在其左侧直接存在一个 空位 时才能向 左 移动,而片段 'R' 只有在其右侧直接存在一个 空位 时才能向 右 移动。
    • 字符 '_' 表示可以被 任意 'L' 或 'R' 片段占据的空位。

    如果在移动字符串 start 中的片段任意次之后可以得到字符串 target ,返回 true ;否则,返回 false 。

    双指针 + 字符串模拟。

    class Solution {
        public boolean canChange(String start, String target) {
            int n = start.length();
            int l = 0;
            for(int r=0;r<n;++r) {
                if(target.charAt(r) == '_') continue;
                else {
                    char ch = target.charAt(r);
                    while(l<n && start.charAt(l) == '_') {
                        ++l;
                    }
                    if(l>=n) return false;
                    if(start.charAt(l)!=target.charAt(r))return false;
                    if(target.charAt(r)=='L' && r>l)return false;
                    if(target.charAt(r)=='R' && r<l)return false;
                    ++l;
                }
            }
            while(l<n) {
                if(start.charAt(l) != '_') return false;
                l++;
            }
            return true;
        }
    }
    

    [Leetcode]2338. 统计理想数组的数目

    给你两个整数 n 和 maxValue ,用于描述一个 理想数组 。
    对于下标从 0 开始、长度为 n 的整数数组 arr ,如果满足以下条件,则认为该数组是一个 理想数组 :

    • 每个 arr[i] 都是从 1 到 maxValue 范围内的一个值,其中 0 <= i < n 。
    • 每个 arr[i] 都可以被 arr[i - 1] 整除,其中 0 < i < n 。

    返回长度为 n 的 不同 理想数组的数目。由于答案可能很大,返回对 \(10^9 + 7\) 取余的结果。

    组合数。分别考虑以x结尾长度为n的理想数组有多少个,数组结尾可以是1 ~ maxValue,因此把这些情况累加,就是最终结果。
    以结尾为4、长度为5进行分析:
    4的前面可以是4、2、1, 2的前面可以是2、1, 1的前面只能是1。例如:
    [1, 2, 2, 4, 4]
    [1, 1, 1, 4, 4]
    [2, 2, 2, 4, 4]
    [4, 4, 4, 4, 4]
    以[1, 2, 2, 4, 4]为例,可以记为 \([\_, *2, \_, *2, \_]\),只需记录在哪些位置的元素发生了改变(倍增),
    从当前倍增的位置开始 ~ 下一次倍增的位置之前(或数组结尾),将会一直维持这个值。
    可假设每个数组的开头前面有一个值1,若数组中的第一个元素为1,则没有发生倍增;若第一个元素不是1,则发生了倍增。
    例如:[2, 2, 2, 4, 4] 可表示为 \([*2, \_, \_, *2, \_]\)
    在同一个位置可以发生多次倍增,例如:[1, 1, 1, 4, 4] 可表示为 \([\_, \_, \_, *2*2, \_]\);[4, 4, 4, 4, 4] 可表示为 \([*2*2, \_, \_, \_, \_]\)
    由于固定了结尾为4,而4的质因子为2、2,即 4 = (1) * 2 * 2
    所有结尾为4、长度为5的理想数组,问题可转化为 结尾数字(4)的质因子可以放在哪些位置,当前有5个不同的位置,2个质因子2,
    从5个位置中选择一个(将2个2放在一个位置)或两个(将2个2放在不同位置),因为2个2是相同的,谁先谁后,结果都是一样的。所以这是个组合问题。
    问题进一步转化为隔板法:把k个相同的小球放进n个不同的盒子中,允许有些盒子为空,也允许一个盒子中放入多个小球,有多少种不同的放法?
    该问题可用隔板法来求解,把n个盒子当做n-1个隔板,然后加上k个小球,相当于总共有 n-1 + k 个位置,从中选出n-1个位置放隔板,
    即方案数为:C(n-1+k)(n-1)
    由于maxValue <= 104,质因子最小为2,213 = 8192 < 10^4 < 16384 = 2^14,质因子越大,质因子的个数将会越小,
    所以质因子为2时,质因子的个数k才能达到最大值13,即 k <= 13。所以上面的 C(n-1+k)(n-1) 可写为 C(n-1+k)(k) ,k 显然远小于n-1.
    若结尾数字由多个不同的质因子组成,例如:k1个2、k2个3、k3个5,则可将问题分解为:
    1、从n-1 + k1个位置中选出k1个位置放质因子2,得到 C(n-1+k1)(k1)
    2、从n-1 + k2个位置中选出k2个位置放质因子3,得到 C(n-1+k2)(k2)
    3、从n-1 + k3个位置中选出k3个位置放质因子5,得到 C(n-1+k3)(k3)
    这3种情况之间互不影响:放质因子5的时候,不用关心这个位置之前放没放过2、3,以及放了多少个2、多少个3。
    所以可采用乘法原理来计算最终结果:C(n-1+k1)(k1) * C(n-1+k2)(k2) * C(n-1+k3)(k3)

    综上,原问题最终转化为:质因数分解出所有的质因子及其个数(其实只关注个数k) + 计算组合数问题
    计算组合数问题 可用动态规划进行计算,假设dp[i][j] 表示从i个位置中选择j个,即 C(i)(j)。该问题可分为两种情况:
    1、选择了位置i,则只需再从i-1个位置中选择j-1个,即 C(i-1)(j-1)
    2、未选择位置i,则需要从i-1个位置中选择j个,即 C(i-1)(j)
    所以,dp[i][j] = dp[i-1][j-1] + dp[i-1][j]

    class Solution {
        static final int MOD = (int) 1e9 + 7, MX = (int) 1e4 + 1, MX_K = 13; // 至多 13 个质因数
        static List[] ks = new List[MX]; // ks[x] 为 x 分解质因数后,每个质因数的个数列表
        static int[][] c = new int[MX + MX_K][MX_K + 1]; // 组合数
    
        static {
            for (var i = 1; i < MX; i++) {
                ks[i] = new ArrayList<Integer>();
                var x = i;
                for (var p = 2; p * p <= x; ++p) {
                    if (x % p == 0) {
                        var k = 1;
                        for (x /= p; x % p == 0; x /= p) ++k;
                        ks[i].add(k);
                    }
                }
                if (x > 1) ks[i].add(1);
            }
    
            c[0][0] = 1;
            for (var i = 1; i < MX + MX_K; ++i) {
                c[i][0] = 1;
                for (var j = 1; j <= Math.min(i, MX_K); ++j)
                    c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % MOD;
            }
        }
    
        public int idealArrays(int n, int maxValue) {
            var ans = 0L;
            for (var x = 1; x <= maxValue; ++x) {
                var mul = 1L;
                for (var k : ks[x]) mul = mul * c[n + (int) k - 1][(int) k] % MOD;
                ans += mul;
            }
            return (int) (ans % MOD);
        }
    }
    

    参考:LeetCode

  • 相关阅读:
    MyBatis学习 之 三、动态SQL语句
    MyBatis学习 之 三、动态SQL语句
    MyBatis学习 之 二、SQL语句映射文件(2)增删改查、参数、缓存
    MyBatis学习 之 二、SQL语句映射文件(2)增删改查、参数、缓存
    Spring3 MVC使用@ResponseBody的乱码问题及解决办法
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
  • 原文地址:https://www.cnblogs.com/hellojamest/p/16489246.html
Copyright © 2020-2023  润新知