整数分区(求将整数拆分成任意个数正整数的方法个数)codewars 爆炸求和

依旧是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](0i(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];
}

然后问题就完美解决了

贴上整数分区的值列表,可以用来调试

整数分区

整数分区(求将整数拆分成任意个数正整数的方法个数)codewars 爆炸求和_第1张图片

你可能感兴趣的:(KATA通关,KATA,codewars,c++)