题目传送门
天啊撸,动态规划找转移方程简直是。。。唉,没办法啊谁叫我菜呢!
题目描述
一个整数总可以拆分为2的幂的和,例如: 7=1+2+4 7=1+2+2+2 7=1+1+1+4 7=1+1+1+2+2 7=1+1+1+1+1+2 7=1+1+1+1+1+1+1 总共有六种不同的拆分方式。 再比如:4可以拆分成:4 = 4,4 = 1 + 1 + 1 + 1,4 = 2 + 2,4=1+1+2。 用f(n)表示n的不同拆分的种数,例如f(7)=6. 要求编写程序,读入n(不超过1000000),输出f(n)%1000000000。
输入描述:
每组输入包括一个整数:N(1<=N<=1000000)。
输出描述:
对于每组数据,输出f(n)%1000000000。
示例1
输入
7
输出
6
动态规划的重点和难点,大都在于转移方程上。我们先来个学术性的论证过程。
以下是我节抄于网络的数学证明:
链接:https://www.nowcoder.com/questionTerminal/376537f4609a49d296901db5139639ec
来源:牛客网
搬运一下思路:
记f(n)为n的划分数,我们有递推公式:
f(2m + 1) = f(2m),
f(2m) = f(2m - 1) + f(m),
初始条件:f(1) = 1。
证明 证明的要点是考虑划分中是否有1。
.
记:
A(n) = n的所有划分组成的集合,
B(n) = n的所有含有1的划分组成的集合,
C(n) = n的所有不含1的划分组成的集合,
则有: A(n) = B(n)∪C(n)。又记:
f(n) = A(n)中元素的个数,
g(n) = B(n)中元素的个数,
h(n) = C(n)中元素的个数,
易知: f(n) = g(n) + h(n)。
.
我们先来证明: f(2m + 1) = f(2m),
首先,2m + 1 的每个划分中至少有一个1,去掉这个1,就得到 2m 的一个划分,故 f(2m + 1)≤f(2m)。
其次,2m 的每个划分加上个1,就构成了 2m + 1 的一个划分,故 f(2m)≤f(2m + 1)。
综上,f(2m + 1) = f(2m)。
.
接着我们要证明: f(2m) = f(2m - 1) + f(m),
把 B(2m) 中的划分中的1去掉一个,就得到 A(2m - 1) 中的一个划分,故 g(2m)≤f(2m - 1)。
把 A(2m - 1) 中的划分加上一个1,就得到 B(2m) 中的一个划分,故 f(2m - 1)≤g(2m)。
综上,g(2m) = f(2m - 1)。
.
把 C(2m) 中的划分的元素都除以2,就得到 A(m) 中的一个划分,故 h(2m)≤f(m)。
把 A(m) 中的划分的元素都乘2,就得到 C(2m) 中的一个划分,故 f(m)≤h(2m)。
综上,h(2m) = f(m)。
.
所以: f(2m) = g(2m) + h(2m) = f(2m - 1) + f(m)。
这就证明了我们的递推公式。
(pa!pa!pa!) 说人话!!!
哎,哎。知错了,知错了!
我首先要再次感谢C姓同学对于我本题上莫大的帮助,不然仅凭我核桃大的脑仁是想不明白的。
以思路过成写的比较详细,略显冗长。但是对于没想明白的童鞋来说,便是再好不过的过程描述了。
第一:我们可以一眼看出来:
f(2m+1)=f(2m)
f
(
2
m
+
1
)
=
f
(
2
m
)
没看出来?
2m+1
2
m
+
1
仅仅比
2m
2
m
的拆分式多了一个 1但是这一个1 他的存在与不存在并不会影响整个式子的拆分式的数量。
①我们让
2m+1
2
m
+
1
的整个拆分式
−1
−
1
得到的就是
2m
2
m
的拆分式,我们知道在对每一个拆分的式子
−1
−
1
(相当于,此时将单出来的那个
1
1
减掉)数量并不会改变,但是我们此时并不知道的数量但是我们
−1
−
1
后得到的式子肯定是他的一个分解式集合里面的一个子集,故有
f(2m)>=f(2m+1)
f
(
2
m
)
>=
f
(
2
m
+
1
)
。
②然后我们让
2m
2
m
的整个拆分式
+1
+
1
就会得到
2m+1
2
m
+
1
的拆分式,我们知道在对每一个拆分的式子加
1
1
(相当于在每一个后面加上一个)数量并不会改变。但是我们此时并不知道
f(2m+1)
f
(
2
m
+
1
)
的数量但是我们
+1
+
1
后得到的式子肯定是他的一个分解式集合里面的一个子集,故有
f(2m)<=f(2m+1)
f
(
2
m
)
<=
f
(
2
m
+
1
)
。
由①②可知 f(2m+1)=f(2m) f ( 2 m + 1 ) = f ( 2 m )
第二:
f(2m)=f(2m−1)+f(m)
f
(
2
m
)
=
f
(
2
m
−
1
)
+
f
(
m
)
我们需要将我们的答案分成俩类:
1.将整数拆分以后会有1的
2.将整数拆分以后没有1的
对于 1 情况的推导:
①因为我们当前我们讨论的变量是偶数的情况所以存在
1
1
的情况必定是至少存在两个,所以我们对于
f(2m)(含1的式子)
f
(
2
m
)
(
含
1
的
式
子
)
每一个式子减少一个
1
1
得到的是的拆分式。我们知道在对每一个拆分的式子减
1
1
(相当于在每一个式子上减去一个)数量并不会改变,但我们此时并不知道
f(2m−1)
f
(
2
m
−
1
)
有多少个拆分式,但是我们至少知道每一个得到的式子都是他的分解式集合的一个子集,故有
:f(2m)(含1的式子)
:
f
(
2
m
)
(
含
1
的
式
子
)
<=
f(2m−1)
f
(
2
m
−
1
)
。
②所以我们对于
f(2m−1)
f
(
2
m
−
1
)
每一个式子加上一个
1
1
得到的是的拆分式。我们知道在对每一个拆分的式子加
1
1
(相当于在每一个式子后面加上一个)数量并不会改变,但我们此时并不知道
f(2m)
f
(
2
m
)
有多少个拆分式,但是我们至少知道每一个得到的式子都是他的分解式集合的一个子集,故有
:f(2m)(含1的式子)
:
f
(
2
m
)
(
含
1
的
式
子
)
>=
f(2m−1)
f
(
2
m
−
1
)
。
由①②可知
f(2m)(含1的式子)
f
(
2
m
)
(
含
1
的
式
子
)
=
f(2m−1)
f
(
2
m
−
1
)
对于 2 情况的推导:
和对情况1的推导是一样的思路,我们可以(不再是加一减一,而是对于每一项
/2
/
2
)
可以得出
f(2m)(不含1的式子)
f
(
2
m
)
(
不
含
1
的
式
子
)
=
f(m)
f
(
m
)
故转移方程:
当n为奇数时
f(n)
f
(
n
)
=
f(n−1)
f
(
n
−
1
)
当n为偶数时
f(n)
f
(
n
)
=
f(n−1)+f(n/2)
f
(
n
−
1
)
+
f
(
n
/
2
)
好吧,代码君:
#include<iostream>
using namespace std;
#define MAXNUM 1000005
int map[MAXNUM];
void init()
{
map[1] = map[0] = 1;
for (int i = 0; i < MAXNUM; i++)
{
if (i % 2 == 1) map[i] = map[i - 1];
else map[i] = (map[i - 1] + map[i / 2])%1000000000;
}
}
int main()
{
int num;
init();
while (cin >> num)
{
cout << map[num] << endl;
}
return 0;
}