(递归、递推)[洛谷P1025 [NOIP2001 提高组] 数的划分]题解|CSDN创作打卡

  今天在学递推和递归,做了这道题。一开始,我想了一个递推的方法。n个数分成k份,那么第一个数可以取1到n-k+1,然后转化为f[n-i][k-1],其中i为第一个位置取的数。但是,题目要求不能重复,一种数字组合只能算一种。这种做法似乎无法排除重复的。
  于是看了题解结合自己的想法,写了一种递归的做法。

#include 
using namespace std;
int ans = 0;
void work(int x, int t, int sum)
{
	if (t == 1)
	{
		ans++;
		return;
	}
	for (int i = x; i <= sum / t; i++)
		work(i, t - 1, sum - i);
}
int main()
{
	int n, k;
	cin >> n >> k;
	work(1, k, n);
	cout << ans;
	return 0;
}

  写的应该挺清晰的,work函数中,x表示上一位取的数字,这样下一个数字必须大于等于x,t表示要分成几份,sum表示还剩下多少数字。
然后看到了洛谷上一个很棒的递推思路。n个数分成k份,有两种情况,至少有一个位置只有一个和所有位置都不止一个。第一种可以转化为一个地方放1个,其他n-1个放在k-1个地方,也就是f[n-1][k-1]。第二种可以让每个位置都拿出一个来,变成了每个位置都不为空,也就是题目的要求,从而变成n-k个放在k个地方,f[n-k][k]。代码如下

#include 
using namespace std;
int main()
{
	int f[201][7]={0};
	f[1][1] = 1;
	for(int i=2;i<=200;i++)
		for (int j = 1; j <= min(6, i); j++)
		{
			f[i][j] = f[i - 1][j - 1] + f[i - j][j];
		}
	int n, k;
	cin >> n >> k;
	cout << f[n][k];
	return 0;
}

  这里还需要注意一下递推边界,哪些地方需要赋初值,这可以根据方程来慢慢推,找到需要我们给出的f[i][j]。同时,枚举j的时候要注意不能超过i,不然i-j会是负数,程序会出错。

你可能感兴趣的:(题解,算法)