用dfs进行整数拆分,对于每种情况计算组合数。为了避免重复计算,必须使用有重集的组合,com(f[i] - 1 + num[i], num[i]),这是num[i]个有i个节点的子树的情况总数。这个公式是用于求所选物品可重复选择,且重复选出的同一物品完全相同的情况。
View Code
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
using namespace std;
#define maxn 41
long long f[maxn], num[maxn], ans;
int n;
long long com(long long n, long long r)
{// return C(n, r)
if (n - r < r)
r = n - r; // C(n, r) = C(n, n-r)
long long i, j, s = 1;
for (i = 0, j = 1; i < r; ++i)
{
s *= (n - i);
for (; j <= r && s % j == 0; ++j)
s /= j;
}
return s;
}
long long gcd(long long a, long long b)
{
return b?gcd(b,a%b):a;
}
long long C(long long n, long long m)
{
if(m > n - m ) m = n - m;
long long mul = 1 , div = 1;
for(int i = 0; i < m;i++)
{
mul *= (n-i);
div *= (i+1);
long long g = gcd(mul,div);
mul/=g , div /= g;
}
return mul / div;
}
void dfs(int s, int left)
{
if (left == 0)
{
ans = 1;
for (int i = 0; i <= n; i++)
if (num[i] != 0)
ans *= com(f[i] - 1 + num[i], num[i]);
f[n] += ans;
}
if (s > left)
return;
num[s]++;
dfs(s, left - s);
num[s]--;
dfs(s + 1, left);
}
int main()
{
//freopen("D:\\t.txt", "r", stdin);
//freopen("D:\\y.txt", "w", stdout);
memset(num, 0, sizeof(num));
f[1] = 1;
f[2] = 1;
for (n = 3; n <= 40; n++)
{
dfs(1, n - 1);
//printf("f[%d]=%I64d;", n, f[n]);
}
while (scanf("%d", &n) != EOF)
printf("%I64d\n", f[n]);
return 0;
}