题干
一个正整数n可以表示成若干个正整数之和,形如:n=n1+n2+…+nk,其中n1≥n2≥…≥nk,k≥1。
我们将这样的一种表示称为正整数n的一种划分。
现在给定一个正整数n,请你求出n共有多少种不同的划分方法。
输入格式
共一行,包含一个整数n。
输出格式
共一行,包含一个整数,表示总划分数量。
由于答案可能很大,输出结果请对109+7取模。
数据范围
1≤n≤1000
输入样例:
5
输出样例:
7
package main
import "fmt"
//方法1
//状态描述:
//集合:f[i][j],整数是i,所有由j个整数组成i的集合
//状态运算:集合分成两部分:整数中最小值是1,和大于1,状态表示:f[i-1][j-1],第二部分,首先所有整数都减去1,f[i-j][j]
const N = 1010
const mod = 1e9+7
var (
n int
//f [N]int
f[N][N]int
)
func main() {
fmt.Scan(&n)
f[0][0]=1 // 特例:整数为0时,0个整数之和表示也是一种方案,所以f[0][0]=1
for i:=1;i<=n;i++{
for j:=1;j<=i;j++{
f[i][j]=(f[i-1][j-1]+f[i-j][j])%mod
}
}
res:=0
for i:=1;i<=n;i++{ // 枚举1-n,得出方案总数
res =(res+f[n][i])%mod
}
fmt.Println(res)
}
// 时间复杂度O(n^2)
参考完全背包问题
/*
思路:把1,2,3, … n分别看做n个物体的体积,这n个物体均无使用次数限制,问恰好能装满总体积为n的背包的总方案数(完全背包问题变形
f[i][j]:表示由1-i之间的数构造整数j
f[i][j]f[i][j] 表示前i个整数(1,2…,i)恰好拼成j的方案数
求方案数:把集合选0个i,1个i,2个i,…全部加起来
f[i][j] = f[i - 1][j] + f[i - 1][j - i] + f[i - 1][j - 2 * i] + ...;
f[i][j - i] = f[i - 1][j - i] + f[i - 1][j - 2 * i] + ...;
因此 f[i][j]=f[i−1][j]+f[i][j−i];f[i][j]=f[i−1][j]+f[i][j−i]; (这一步类似完全背包的推导)
*/
朴素做法
package main
import "fmt"
const N=1010
const mod=1e9+7
// 类似背包问题的处理
// f[i][j],由1-i之间的数构造整数j
var (f[N][N]int
n int )
func main(){
fmt.Scan(&n)
for i:=0;i<=n;i++{
f[i][0]=1
}
for i:=1;i<=n;i++{
for j:=0;j<=n;j++{
f[i][j]=f[i-1][j]
// f[i][j]=(f[i-1][j-1]+f[i-j][j])%mod
if j>=i{
f[i][j]=(f[i][j]+f[i][j-i])%mod
}
}
}
// res:=0
// for i:=1;i<=n;i++{
// res=(res+f[n][i])%mod
// }
// fmt.Println(res)
fmt.Println(f[n][n])
}
优化一维度做法
package main
import "fmt"
const N=1010
const mod=1e9+7
// 类似背包问题的处理
// f[i][j],由1-i之间的数构造整数j
var (f[N]int
n int )
func main(){
fmt.Scan(&n)
f[0] =1
for i:=1;i<=n;i++{
for j:=i;j<=n;j++{
f[j]=(f[j]+f[j-i])%mod
}
}
fmt.Println(f[n])
}