http://acm.timus.ru/problem.aspx?space=1&num=1017
给出一个数n,可以把它表示成若干个整数的和。要求这些数各不相同,且至少两个。
一道类似整数划分的问题。
不知道这个知识点真的不好办,想半天愣是想不出头绪。
p[n][k]表示和为n且最小被加数不小于k的表示方法数。
那么dp[n][k] = dp[n-k][k+1] + dp[n][k+1]。
#include <stdio.h> #include <string.h> #include <algorithm> #define LL __int64 using namespace std; LL p[510][510]; int n; int main() { while(~scanf("%d",&n)) { for(int i = 1; i <= n; i++) p[i][i] = 1; for(int i = 2; i <= n; i++) { for(int k = i-1; k >= 1; k--) p[i][k] = p[i-k][k+1] + p[i][k+1]; } printf("%I64d\n",p[n][1]-1); } return 0; }
数 n 的分划(partition)是将 n 表示成任意多个正整数之和的形式。例如,数 5 的分划如下:
用 p(n) 来记 n 的分划的个数,这样就有 p(5) = 7。
为了求解 p(n),我们引入一个中间函数 p(k, n),表示数 n 的最小被加数不小于 k 的分划的个数。对于给定的 k 值,p(k, n) 正好分为以下两类:
满足第一个条件的分划的个数是 p(k, n − k)。 这是因为,让我们想象数 n − k 的最小被加数不小于 k 的分划,然后将 "+ k" 附加每一个分划后面,就得到数 n 的最小被加数等于 k 的分划。以 n = 5, k = 1 为例,数 4 的最小被加数不小于 1 的分划是 4、3 + 1、2 + 2、2 + 1 + 1 和 1 + 1 + 1 + 1,即 p(k, n − k) = p(1, 4) = 5。然后,将 "+ 1" 附加在这 5 个分划后面,就得到数 5 的最小被加数等于 1 的分划:4 + 1、3 + 1 + 1、2 + 2 + 1、2 + 1 + 1 + 1 和 1 + 1 + 1 + 1 + 1。
满足第二个条件的分划的个数是 p(k + 1, n) 。以 n = 5, k = 1 为例,数 5 的最小被加数大于 1 的分划是 5 和 3 + 2,即 p(k + 1, n) = p(2, 5) = 2。
也就是说,p(1, 5) = p(2, 5) + p(1, 4)。因此:
这样,就可以递归地求解 p(k, n),其部分值见下表:
k | |||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | ||
n | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
2 | 2 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | |
3 | 3 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | |
4 | 5 | 2 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | |
5 | 7 | 2 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | |
6 | 11 | 4 | 2 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | |
7 | 15 | 4 | 2 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | |
8 | 22 | 7 | 3 | 2 | 1 | 1 | 1 | 1 | 0 | 0 | |
9 | 30 | 8 | 4 | 2 | 1 | 1 | 1 | 1 | 1 | 0 | |
10 | 42 | 12 | 5 | 3 | 2 | 1 | 1 | 1 | 1 | 1 |
最后,p(n) = p(1, n)。
现在,让我们的来考虑 将 n 分成不相等的正整数之和的分划。例如,数 8 的分划如下:
用 q(n) 来记 n 的分划的个数,这样就有 q(8) = 6。
为了求解 q(n),我们引入一个中间函数 q(k, n),表示数 n 的最小被加数不小于 k 的分划的个数。对于给定的 k 值,q(k, n) 正好分为以下两类:
满足第一个条件的分划的个数是 q(k + 1, n − k)。 这是因为,让我们想象数 n − k 的最小被加数大于 k 的分划,然后将 "+ k" 附加每一个分划后面,就得到数 n 的最小被加数等于 k 的分划。以 n = 8, k = 1 为例,数 7 的最小被加数大于 1 的分划是 7、5 + 2 和 4 + 3,即 q(k + 1, n − k) = q(2, 7) = 3。然后,将 "+ 1" 附加在这 3 个分划后面,就得到数 8 的最小被加数等于 1 的分划:7 + 1、5 + 2 + 1 和 4 + 3 + 1。
满足第二个条件的分划的个数是 q(k + 1, n) 。以 n = 8, k = 1 为例,数 8 的最小被加数大于 1 的分划是 8、6 + 2 和 5 + 3,即 q(k + 1, n) = q(2, 8) = 3。
也就是说,q(1, 8) = q(2, 8) + q(2, 7)。因此:
这样,就可以递归地求解 q(k, n),其部分值见下表:
k | |||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | ||
n | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
2 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | |
3 | 2 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | |
4 | 2 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | |
5 | 3 | 2 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | |
6 | 4 | 2 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | |
7 | 5 | 3 | 2 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | |
8 | 6 | 3 | 2 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | |
9 | 8 | 5 | 3 | 2 | 1 | 1 | 1 | 1 | 1 | 0 | |
10 | 10 | 5 | 3 | 2 | 1 | 1 | 1 | 1 | 1 | 1 |
最后,q(n) = q(1, n)。
定理 344 将 n 分成若干个不相等的数之和的分划个数等于将它分成若干个奇数之和的分划个数。
这个定理是伟大的数学家欧拉(Leonard Euler)在1748年首先证明的。