程序基本算法习题解析 动态规划-数的划分 将整数n分成k份,且每份不能为空,任意两种划分方案不能相同(不考虑顺序)。求整数n分为k份,共有多少种不同的分法。

题目:

将整数n分成k份,且每份不能为空,任意两种划分方案不能相同(不考虑顺序)。求整数n分为k份,共有多少种不同的分法。输入两个整数n,k(6<n<=200,2<=k<=6)。输出一个整数,即有几种不同的分法。

思路:

定义一个数组dp[][],dp[i][j]表示将整数 i 划分为 j 份 的方案数。dp[i][j]的动态转移方程为 :

如何理解该式子呢?首先,如果拿到一个整数 i ,因为题目中要求每份不能为空,因此必须先拿出 j 个数位将 j 份分别放上1,此时剩下 i - j个数。那么剩下的数如何处理呢?可以将其全部分到一份当中(dp[i-j][1]),也可以分到两份中(dp[i-j][2]),...,也可以分到 j 份中(dp[i-j][j]),而每一种分法都是不相同的,所以可以将其全部加起来,和即为dp[i][j]。

不过这个式子看起来并不简洁,为了求得一个简洁的式子,我们再求一个dp[i-1][j-1],

比较上面两个式子可得,

这就是一个比较简洁的递推式子了,有点类似于高中数学中的裂项相消原理。 

这里注意 i , j 的取值范围,i = 1~n,j = 1~ k,但是求dp[i][j]时,必须保证 i>=j(划分的份数不能超过给定的整数)。

另外就是,做了好几道动态规划的题,发现动态转移方程真的好重要(也可以说是递推公式),看似很难很绕的题目,只要提炼出它的动态转移方程,搞清楚遍历的次序以及各变量的取值范围,写程序就相当快了。

代码如下:

// Chapter14_5.cpp : Defines the entry point for the application.
// 数的划分
// 将整数n分成k份,且每份不能为空,任意两种划分方案不能相同(不考虑顺序)。
// 求整数n分为k份,共有多少种不同的分法。
// 输入两个整数n,k(6<n<=200,2<=k<=6)。
// 输出一个整数,即有几种不同的分法。

#include "stdafx.h"
#include<iostream>
using namespace std;

int main()
{
	int n,k;
	cout << "输入整数n和划分的份数k:";
	cin >> n >> k;
	int dp[201][7];
	//全部初始化为0
	memset(dp,0,sizeof(dp));
	//对于dp[0][0]作特殊处理(为了后面的动态转移方程能够起作用)
	dp[0][0] = 1;
	int i,j;
	//i的取值范围是1~n(不能超过给定的整数)
	for(i=1;i<=n;i++)
		//j的范围是1~k(不能超过需要划分的份数)
		for(j=1;j<=k;j++)
		{
			//划分的分数要小于等于该数本身
			if(i>=j)
				//动态转移方程
				dp[i][j] = dp[i-j][j] + dp[i-1][j-1];
		}
	cout << "共有" << dp[n][k] << "种分法" << endl;
	system("pause");
	return 0;
}

运行结果如下:

猜你喜欢

转载自blog.csdn.net/elma_tww/article/details/86538836