动态规划+组合数学

这篇博客介绍了如何解决LeetCode上的一道题目,涉及理想数组的概念。理想数组需要满足每个元素是前一个元素的倍数,并在给定范围内。文章通过动态规划和组合数学的方法,详细解析了如何计算不同理想数组的数目,最终实现了O(14nln(n))的时间复杂度。此外,还讨论了如何处理包含倍数为1的情况,利用隔板法和组合数学优化解决方案。
摘要由CSDN通过智能技术生成

leetcode 统计理想数组的数目

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

  • 每个 a r r [ i ] arr[i] arr[i] 都是从 1 1 1 m a x V a l u e maxValue maxValue 范围内的一个值,其中 0 < = i < n 0 <= i < n 0<=i<n
  • 每个 a r r [ i ] arr[i] arr[i] 都可以被 a r r [ i − 1 ] arr[i - 1] arr[i1] 整除,其中 0 < i < n 0 < i < n 0<i<n
    返回长度为 n n n 的 不同 理想数组的数目。由于答案可能很大,返回对 1 0 9 + 7 10^9 + 7 109+7 取余的结果。

思路

理想数组满足:数组里的每个数都在 [ 1 , m a x V l a u e ] [1,maxVlaue] [1,maxVlaue],且每个数都是前一个数的倍数(倍数包括1)
首先考虑一个简化版本,当每个数都是前一个数的倍数,倍数不包括1,即数组严格递增。因为 m a x V a l u e ≤ 1 e 4 maxValue\leq 1e4 maxValue1e4,所以此时数组的长度最多为 14 14 14( 2 13 = 8192 2^{13}=8192 213=8192),所以可以使用动态规划,状态 f [ i ] [ j ] f[i][j] f[i][j]表示以 i i i开头的,长度为 j j j的数组方案数。所以 f [ i ] [ j ] = s u m ( f [ i ∗ k ] [ j − 1 ] ) f[i][j]=sum(f[i*k][j-1]) f[i][j]=sum(f[ik][j1]),其中 i ∗ k ≤ m a x V a l u e i*k \leq maxValue ikmaxValue,这层循环的时间复杂度为 1 + 1 / 2 + 1 / 3 + . . . + 1 / n = l n ( n ) + C 1+1/2+1/3+...+1/n=ln(n)+C 1+1/2+1/3+...+1/n=ln(n)+C。所以整体的时间复杂度为 O ( 14 n l n ( n ) ) O(14nln(n)) O(14nln(n))。此时求出了严格递增的理想数组个数。但是理想数组中存在前后元素倍数为1的情况,此时使用组合数学的知识,对 f [ i ] [ j ] f[i][j] f[i][j],可以使用隔板法将长度为 n n n的数组隔成 j j j段,那么共有 f [ i ] [ j ] ∗ C n − 1 j − 1 f[i][j]*C_{n-1}^{j-1} f[i][j]Cn1j1,最后将每个状态累加即可。
对于 C n m C_n^m Cnm,有 C n m = C n − 1 m − 1 + C n − 1 m C_n^m=C_{n-1}^{m-1}+C_{n-1}^m Cnm=Cn1m1+Cn1m

代码

class Solution {
    public int idealArrays(int n, int maxValue) {
        int MOD = (int)(1e9 + 7);
        int[][] cnt = new int[maxValue + 1][15];
        for(int i = 1; i <= maxValue; i++) {
            cnt[i][1] = 1;
        }
        for(int i = 2; i < 15; i++) {
            for(int j = 1; j <= maxValue; j++) {
                for(int k = 2; k * j <= maxValue; k++) {
                    cnt[j][i] = (cnt[j][i] + cnt[j * k][i-1]) % MOD;
                }
            }
        }
        int[][] C = new int[n][15];
        for(int i = 0; i < n; i++) {
            C[i][0] = 1;
        }
        for(int i = 1; i < n; i++) {
            for(int j = 1; j < 15; j++) {
                C[i][j] = (C[i-1][j-1] + C[i-1][j]) % MOD;
            }
        }
        int res = 0;
        for(int i = 1; i <= maxValue; i++) {
            for(int j = 1; j < 15; j++) {
                int x = (int)((long)cnt[i][j] * C[n-1][j-1] % MOD);
                res = (res + x) % MOD;
            }
        }
        return res;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值