【动态规划C语言】求解整数拆分问题 原理+代码

原理

设f(n,k)为将整数n无序拆分成最多不超过k个数之和的方案个数

根据n和k的关系,考虑下面几种情况:

(1)当n=1时,不论k的值为多少(k>0),只有一种拆分,即{1};

        显然 f(n,k)=1 

(2)当k=1时,不论n的值为多少(n>0),只有一种拆分,即{1,1,....1,1,1};

        显然 f(n,k)=1 

(3)当n=k时,根据拆分中是否包含n,可以分为两种情况:

      1.拆分中包含n的情况,只有一个,即{n};
      2.拆分中不包含n的情况,这时拆分中最大的数字也一定比n小,即n的所有(n-1)拆分;
      因此,f(n,n) = 1 + f(n, n - 1)

(4)当n<k时,由于拆分中不可能出现负数,因此就相当于f(n,n);

(5)当n>k时,根据拆分中是否包含k,可以分为两种情况:

      1.拆分中包含k的情况,即一部分是单个k,另一部分是{x1,x2,x3,...,xi},即{k,{x1,x2,x3,...,xi}},其中{x1,x2,x3,...,xi}的和为n-k,可能再次出现k,因此是(n-k)的k拆分,因此这种拆分个数为f(n-k, k);
      2.拆分中不包含k的情况,则拆分中所有值都比k小,即n的(k-1)拆分,个数为f(n, k - 1);
 因此,f(n,k) = f(n - k,k) + f(n, k - 1)


图解如下

 该问题实际上本身是递归,但因为子问题重叠,递归会有大量重复的计算出现,所以设置二维数组dp[n][k]存放f(n,k), 首先初始化dp中所有元素为特殊值0,当dp[n][k]不为0时表明子问题已求解,直接返回结果,这种方法也叫备忘录方法,是动态规划的一种变形。

完整代码如下: 

#include <stdio.h>
#include <string.h>
#define MAXN 500
int dp[MAXN][MAXN]; 
int dpn(int n,int k){
	if(dp[n][k]!=0) return dp[n][k]; 
	if(n==1||k==1) return dp[n][k]=1;
	else if(n<k) return dp[n][k]=dpn(n,n);
	else if(n==k) return dp[n][k]=dpn(n,k-1)+1;
	else return dp[n][k]=dpn(n,k-1)+dpn(n-k,k);
}
int main(){
	int n=6,k=5;
	memset(dp,0,sizeof(dp));  //初始化为0 
	printf("dpn(%d,%d)=%d种\n",n,k,dpn(n,k));
}

猜你喜欢

转载自blog.csdn.net/KK_1657654189/article/details/120845403