LeetCode 2338 统计理想数组的数目

本文介绍了如何使用多项式动态规划解决LeetCode题目2338,通过找到理想数组的规律,将问题转化为求解多项式表达式的系数,从而实现高效求解并避免TLE。关键步骤包括构建动态规划矩阵和求解线性方程组。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

LeetCode 2338 统计理想数组的数目


题目链接


题目简介

给你两个整数 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 的 不同 理想数组的数目。由于答案可能很大,返回对 1 0 9 + 7 10^9 + 7 109+7 取余的结果。

提示

  • 2 < = n < = 1 0 4 2 <= n <= 10^4 2<=n<=104
  • 1 < = m a x V a l u e < = 1 0 4 1 <= maxValue <= 10^4 1<=maxValue<=104

解题思路


递归函数(TLE)

l a s t V a l u e lastValue lastValue指数组首元素必须为 l a s t V a l u e lastValue lastValue的倍数

  • 对于求解长度为 n n n l a s t V a l u e lastValue lastValue l l l m a x V a l u e maxValue maxValue k k k的问题 f ( n , l , k ) f(n,l,k) f(n,l,k),可以规约为求解
    ∑ i = l k f ( n − 1 , i , k ) , 其中 i   m o d   l = 0 \sum_{i=l}^{k}f(n-1,i,k) , 其中i\ mod\ l=0 i=lkf(n1,i,k),其中i mod l=0
//cpp
class Solution {
public:
    int mod = 1e9 + 7;
    long long solve(int n,int lastValue,int maxValue){
        if(n==0)return 1;
        long long cnt = 0;
        for(int i=lastValue;i<=maxValue;i+=lastValue){
            cnt += solve(n-1,i,maxValue);
            cnt %= mod;
        }
        return cnt;
    }
    int idealArrays(int n, int maxValue) {
        return solve(n,1,maxValue);
    }
};
  • 由于递归函数对于相同的问题会重复计算,且有较高的时间复杂度,最终结果为TLE

动态规划(TLE)

  • 动态规划数组 d p [ n ] [ m a x V a l u e + 1 ] dp[n][maxValue+1] dp[n][maxValue+1]
  • 转移公式
    d p [ i ] [ j ] = ∑ k = j m a x V a l u e d p [ i − 1 ] [ k ] , 其中 k   m o d   j = 0 dp[i][j] = \sum_{k=j}^{maxValue}dp[i-1][k] ,其中k\ mod\ j=0 dp[i][j]=k=jmaxValuedp[i1][k],其中k mod j=0
  • 最终结果为
    ∑ j = 1 m a x V a l u e d p [ n − 1 ] [ j ] \sum_{j=1}^{maxValue}dp[n-1][j] j=1maxValuedp[n1][j]
  • 采用了滚动数组的思路
//cpp
class Solution {
public:
	int idealArrays_ref(int n, int maxValue) {
	    //dp
	    int dp[2][maxValue+1],t,i,j,k,idx1,idx2,ret;
	    for(i=1;i<=maxValue;i++)dp[0][i] = 1;
	    for(i=1;i<n;i++){
	        idx1 = i%2;
	        idx2 = !idx1;
	        for(j=1;j<=maxValue;j++){
	            t=0;
	            for(k=j;k<=maxValue;k+=j){
	                t += dp[idx2][k];
	                t %= mod;
	            }
	            dp[idx1][j] = t;
	        }
	    }
	    ret = 0;
	    idx1 = (n-1)%2;
	    for(i=1;i<=maxValue;i++){
	        ret += dp[idx1][i];
	        ret %= mod;
	    }
	    return ret;
	}
}
  • 由于动态规划同样有较大的时间复杂度 O ( n ∗ m a x V a l u e ∗ l n ( m a x V a l u e ) ) O(n*maxValue*ln(maxValue)) O(nmaxValueln(maxValue)),最终结果为TLE

待定系数法(AC)

  • 不难(hennan)发现最终的结果可以用多项式表示,即
    ∑ i = 0 e x p k i ∗ x i (1) \sum_{i=0}^{exp}k_i*x^i \tag{1} i=0expkixi(1)
    其中 2 e x p < = m a x V a l u e 且 2 e x p + 1 > m a x V a l u e , x = n + 1 , k i 为常数项 其中2^{exp}<=maxValue 且2^{exp+1}>maxValue ,\\x=n+1,k_i为常数项 其中2exp<=maxValue2exp+1>maxValuex=n+1ki为常数项
  • 这样我们就可以通过动态规划求得 n ∈ [ 0 , e x p ] n\in[0,exp] n[0,exp]时公式(1)的值,因此我们就得到了 e x p + 1 exp+1 exp+1个含有 e x p + 1 exp+1 exp+1个未知数(常数项 k i k_i ki)的线性方程组

n = 0 n=0 n=0时公式(1)值为1,即只存在一个空数组

  • 通过求解线性方程组便获得了所有系数的值,将 x = n + 1 x=n+1 x=n+1代入公式(1)即可求得结果
  • 由于本方法所需精度较高,所以使用了java的BigDecimal
//java
import java.math.BigDecimal;

class Solution {
	static int mod = (int)1e9+7;
	static long dp[][];
	static final int div_precison = 256;

	public static void linear_equ(BigDecimal coeff[],int n) {
		
		//double data[][] = new double[n][n+1];
		BigDecimal data[][] = new BigDecimal[n][n+1];
		for(int i=0;i<n;i++) {
			for(int j=0;j<n+1;j++) {
				data[i][j] = new BigDecimal(0);
			}
		}
		for (int i = 0;i < n;i++){
			for (int k = 0;k < n+1;k++){
				if(k<n) { //data[i][k] = Math.pow(i+1,k);
					data[i][k] = new BigDecimal(i+1);
					data[i][k] = data[i][k].pow(k);
				}
				else{ 	
					data[i][k] = new BigDecimal(dp[i][1]); 
				}
			}
		}
		for (int k = 0;k < n;k++){
			for (int j = k + 1;j < n;j++){
				BigDecimal t1 = new BigDecimal(0).add(data[j][k]);
				BigDecimal t2 = new BigDecimal(0).add(data[k][k]);
				for (int m = 0;m < n+1;m++){
					//data[j][m] -= data[k][m] * data[j][k] / data[k][k];
					data[j][m] = data[j][m].subtract(
							data[k][m].multiply(t1).divide(t2));
				}

			}
		}
	    for (int i = n-1;i >= 0;i--){
			BigDecimal t = new BigDecimal(0);
			for (int k = 0;k < n;k++){
				if (k == i)continue;
				t = t.add(coeff[k].multiply(data[i][k]));
			}
			coeff[i] = data[i][n].subtract(t).
					divide(data[i][i],div_precison,BigDecimal.ROUND_DOWN) ;       
		}
	}

	public static int pre(int num){
		int ret = 0;
		while(Math.pow(2,ret)<=num)ret++;
		return ret-1;
	}

	public static int idealArrays(int n, int maxValue) {
		int exp_num = pre(maxValue);
		dp = new long[exp_num+1][maxValue+1];
		double startTime,endTime;
		startTime = System.currentTimeMillis();
	    for(int i=1;i<=maxValue;i++)dp[0][i] = 1;
	    for(int i=1;i<=exp_num;i++){
	        for(int j=1;j<=maxValue;j++){
	            long t=0;
	            for(int k=j;k<=maxValue;k+=j){
	                t += dp[i-1][k];
	            }
	            dp[i][j] = t;
	        }
	    }
	    endTime = System.currentTimeMillis();
	    System.out.println("elapsed time(bottleneck):"+(endTime-startTime)+"ms");
	    BigDecimal coeff[] = new BigDecimal[exp_num+1]; // coefficient array
	    for(int i=0;i<coeff.length;i++) {
	    	coeff[i] = new BigDecimal(0);
	    }
	    linear_equ(coeff,exp_num+1);
	    BigDecimal ret_num = new BigDecimal(0);
	    BigDecimal MOD = new BigDecimal(mod);
	    for(int i=0;i<coeff.length;i++){ //ret_num += pow(n+1,i) * ret[i];
	    	BigDecimal t1 = new BigDecimal(n+1);
	    	t1 = t1.pow(i);
	    	ret_num = ret_num.add(t1.multiply(coeff[i]));
	    	ret_num = ret_num.remainder(MOD);
		}
		System.out.println(ret_num);
		return ret_num.add(new BigDecimal("0.5")).intValue();//round(ret_num)
	}
	public static void main(String[] args) {
		double startTime,endTime;
		startTime = System.currentTimeMillis();
		System.out.println(idealArrays((int)1e6,(int)1e6));
		endTime = System.currentTimeMillis();
		System.out.println("elapsed time(tot):"+(endTime-startTime)+"ms");
	}
}

  • 最终成功AC,时间复杂度为 O ( m a x V a l u e ∗ l n ( m a x V a l u e ) ) O(maxValue*ln(maxValue)) O(maxValueln(maxValue)),主要瓶颈在于求 n ∈ [ 0 , e x p ] n\in[0,exp] n[0,exp]时公式(1)的值
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值