依旧是codewars上面的题目,献上链接
codewars 爆炸总和
整数n的分区是正整数(不一定是不同的)的无序集合,其加起来为n。这种分区的数量通常表示为p(n)。函数p称为分区函数,其值列表如下。这从p(0)= 1开始,因为只有一个正整数的集合,其总和为零(即空集合)。
n:p(n)
0:1
1:1 //1
2:2 //1+1 2
3:3 //1+1+1 1+2+1 3
4:5 //1+1+1+1 1+1+2 3+1 2+2 4
5:7 //1+1+1+1+1 1+1+1+2 1+1+3 1+2+2 4+1 2+3 5
6:11 //1+1+1+1+1+1 1+1+1+1+2 1+1+1+3 1+ 1+2+2 1+1+4 1+2+3 1+5 2+2+2 2+4 3+3 6
...
在写代码之前应该先搞懂数学分区的数学规律。
假设将n进行分区,通过规律我们可以知道n各分区中包含n-1的分区加上1,整理之后得出以下规律:
p(n) = p(n-1)
+p(n-2) - p(n-2)中有“+1”的分区方法
+p(n-3) - p(n-3)中有“+1”的分区方法 - p(n-3)中有“+2”的分区方法
…
直到 n-i < i
p(n) | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
---|---|---|---|---|---|---|---|---|---|---|
1 | 2 | 3 | 5 | 7 | 11 | 15 | 22 | 30 | 42 |
w[i][n] | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 规律 |
---|---|---|---|---|---|---|---|---|---|---|---|
+0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
+1 | 0 | 1 | 2 | 3 | 5 | 7 | 11 | 15 | 22 | 30 | p(n-1) |
+2 | 0 | 0 | 0 | 1 | 1 | 2 | 2 | 4 | 4 | 7 | w[i-1][n-1]-w[i-1][n-2] |
+3 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 2 | 2 | w[i-1][n-1]-w[i-1][n-3] |
+4 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | w[i-1][n-1]-w[i-1][n-4] |
+5 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | w[i-1][n-1]-w[i-1][n-5] |
… | |||||||||||
+i | w[i-1][n-1]-w[i-1][n-i] |
则
p ( n ) = ∑ w [ i ] [ n ] ( 0 ≤ i ≤ ( n + 1 ) / 2 ) p(n)=\sum w[i][n](0≤i≤(n+1)/2) p(n)=∑w[i][n](0≤i≤(n+1)/2)
找出规律之后我们就可以写代码了
可以看出解决整数分区最重要的两个函数一个是计算p(n)的求和公式,一个是计算w[n][i]的函数。
//p(n)加和函数
using ull = unsigned long long;
ull exp_sum(ull n)
{
ull num = 0;
for (ull i = 0; i <= n/ 2; i++)//当i>n/2,w[n][i]=0
{
num += cal_w(n, i);
}
sum_vec.push_back(num);
return num;
}
//w[n][i]的计算函数
ull cal_w(ull n, ull i)
{
if (n==0||i==0)
{
return 1;
}
if (i==1)
{
return sum_vec[n - 1];
}
if (i>n/2)
{
return 0;
}
return add(n - 1, i - 1) - add(n - i, i - 1);
};
在页面上提交的时候发现有超时,那么说明要改数据结构了。
解决函数运行性能,常常用内存换运行时间,也就是将以前计算过得数据存储出来,方便使用。
加入两个容器:
vectorp{1,1,2};
vector>w{ {0},{1},{1,1} };
代码更改如下:
//前向声明
ull exp_sum(ull n);
ull cal_w(ull n, ull i);
//w[n][i]的计算函数
ull cal_w(ull n, ull i)
{
if (n>=w.size())//如果w中没有n的相关加和数据,先进行w[n]的计算
{
w.push_back(vector<ull>(n, 0));
w.back()[0] = 1;
w.back()[1] = p[n - 1];
for (ull i = 2; i <= n / 2; i++)
{
w.back()[i]= cal_w(n - 1, i - 1) - cal_w(n - i, i - 1);
}
}
if (i>=w[n].size()&&i>n/2)
{
return 0;
}
return w[n][i];//返回w[n][i]
};
//p(n)加和函数
ull exp_sum(ull n)
{
while (n>=p.size())
{
ull num = 0;
for (ull i = 0; i <= p.size()/ 2; i++)//p[n]不存在时,先将n以及n以前的所有数据补齐
{
num += cal_w(p.size(), i);
}
p.push_back(num);
}
return p[n];
}
然后问题就完美解决了
贴上整数分区的值列表,可以用来调试
整数分区