专栏文章

划分数求法

算法·理论参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@mioihtbx
此快照首次捕获于
2025/12/02 19:45
3 个月前
此快照最后确认于
2025/12/02 19:45
3 个月前
查看原文
划分数是一个非常常见的问题。
给定一个整数 nn,求有多少种划分方式。
4=3+1=2+1+14=3+1=2+1+1

一、O(n2)O(n^2) 算法

设把 ii 划分成 jj 个数有 f(i,j)f(i,j) 种分法。

1.完全背包

直接 DP 每个数字取了多少次
f(i,j)=f(ik,j1)f(i,j)=f(i-k,j-1)
由于我们并不在意具体取了多少次,所以可以把 jj 去掉。时间复杂度为 O(nk)O(nk)kk 为数字种类数。

2.构造法

在构造法中,为了方便,我们允许划分出 0。f(n+m,m)=f(n,m)f'(n+m,m)=f(n,m)
我们考虑一个划分序列可以怎么构造出来
  1. 序列后面加一个 00
  2. 把序列所有数 +1+1
容易证明,每个拆分序列都会被有且仅有一个构造方式构造出来
所以我们得到转移方程:
f(i,j)=f(i,j1)+f(ij,j)f(i,j)=f(i,j-1)+f(i-j,j)
时间复杂度为 O(nm)O(nm)mm 为拆分序列最大长度。

二、O(nn)O(n\sqrt n) 算法

我们发现,当取的数字很多时,取的数字就很小;取的数字很大时,取的数字就很少。所以我们能不能结合以上两种算法呢?
我们取一个临界值 BB
我们考虑分开处理只取 [1,B)[1,B) 的情况和只取 [B,n][B,n] 的情况。再把两边的答案拼在一起就是最终答案。
只选 [1,B)[1,B) 时,取的数字很小,用背包的方法就能 O(nB)O(nB) 求出答案。
只选 [B,n)[B,n) 时,取的数字很少,不会超过 n/Bn/B 个,用构造法就能 O(n2B)O(\dfrac{n^2}{B}) 求出答案。
拼在一起是 O(n)O(n) 的,所以总的时间复杂度为 O(nB+n2B)O(nn)O(nB+\dfrac{n^2}{B})\ge O(n\sqrt n)B=(n)B=\sqrt(n)

代码:

CPP
int B=sqrt(n);
f[0]=1;
for(int i=1;i<B;i++)
  for(int j=i;j<=n;j++)
    f[j]=(1ll*f[j]+f[j-i])%mod;
g[0]=1;
for(int i=1;i<=n/B;i++){
	for(int j=0;j<=n;j++){
		if(j>=i)g[j]=(1ll*g[j]+g[j-i])%mod;
		if(j+i*B<=n)ans=(1ll*ans+1ll*g[j]*f[n-(j+i*B)])%mod;
	}
}
注意代码中因为要省空间所以使用了滚动数组,数组 g 交换了两维。
还有一种方法是五边形数,可是我不会qwq。

评论

0 条评论,欢迎与作者交流。

正在加载评论...