首先来了解整数拆分的创始人—— 拉马努金。
这位是我数学上最佩服的几个大神之一,当然最佩服的还是欧拉,欧了~
如果提前了解了离散数学里的集合划分知识,那下面内容会更加易懂。
问题描述:
有一个正整数n,可以拆分成最大数为m的的拆分方案,要求所有的方案都不重复。
例如n = 4, m = 4,拆分方案如下:
4 = 4;
4 = 1 + 3;
4 = 2 + 2;
4 = 1 + 2 + 1;
4 = 1 + 1 + 1 + 1;
首先,我们先假设n和m的大小。
- n = 1,m>0;则f(n,m) = 1,即{1};
- n > 0,m = 1;则f(n,m) = 1,即{1, 1, 1, 1, …};
- n < m;f(n, m) = f(n, n);
- n = m;
(1)n的划分包含m时,则f(n,m) = 1,即{m};
(2)n的划分不包含m时,则f(n,m) = f(n, m -1),即{n1, n2, n3…};
即n = m时,f(n, m) = f(n, m - 1) + 1; - n > m;
(1)n的划分包含m时,则f(n,m) = f(n - m, m), 即{m, n1, n2,…},其中n1 + n2 + … = n - m,其中可能还会出现m;
(2)n的划分不包含m时,则f(n, m) = f(n, m - 1);
即n > m时,f(n, m) = f(n, m - 1) + f(n - m, m);
流程图为:
递归法
ll PartitionCount(ll n, ll m)
{
if(n == 1 || m == 1)
return 1;
else if(n < m)
return PartitionCount(n , n);
else if(n == m)
return PartitionCount(n , n - 1) + 1;
else
return PartitionCount(n - m , m) + PartitionCount(n , m - 1);
}
由于递归法可能会重复计算,耗时方面会巨大,所以就有下面的动态规划。
动态规划
ll dp[10005][10005];
void Partition_DP(ll n, ll m)
{
for(ll i = 1;i <= n + 1; i++)
{
for(ll j = 1;j <= m + 1; j++)
{
if(i == 1 || j == 1)
dp[i][j] = 1;
else if(i == j)
dp[i][j] = 1 + dp[i][j - 1];
else if(i < j)
dp[i][j] = dp[i][i];
else
dp[i][j] = dp[i - j][j] + dp[i][j - 1];
}
}
}
整数拆分和排列组合息息相关,排列组合里的隔板法就和这个非常像,但是到该用的时候就不会用了…