算法学习->整数拆分问题
动态规划典型题目/
00 题目
将正整数n无需拆分为最大数为k的拆分方案有多少种?
要求所有的拆分方案不重复。
示例:
输入:n=5,k=5
输出:(5,5)=7
示例分析:
-
5=5
-
5=4+1
-
5=3+2
-
5=3+1+1
-
5=2+2+1
-
5=2+1+1+1
-
5=1+1+1+1+1
所以一共七种。
01 思路
01-1 类型
求解这一问题的思路有很多,在这里是想作为动态规划的例题进行分析。所以采用动态规划。
01-2 算法
使用动态规划,最重要的是状态转移方程
简单说就是当前状态对下一状态,依据限制条件,进行决策的函数。
来想一下,n和k的关系:
-
n=1或者k=1时,显然 f(n,k)=1;
-
n<k时,有 f(n,k)=f(n,n);
-
n=k时,f(n,n)=f(n,n-1)+1
-
//即将k降一次的感觉,因为这种情况必有一种拆分是他自己,比如上面的5,5
-
-
n>k时,我们考虑一下,会有两种大的情况:
-
第一种,n的拆分里有k,那这一大类其实相当于f(n,k)=f(n-k,k)
-
第二种,n的拆分里没k,那就是拆分里所有数都比k小,即n的(k-1)的拆分,即f(n,k)=f(n,k-1);
-
这样的代码就很好实现。
1 //求解n的k拆分 2 #include<stdio.h> 3 #include<string.h> 4 #define MAXN 500 5 int dp[MAXN][MAXN]; 6 void Split(int n, int k){ 7 for(int i = 1; i <= n; i++){ 8 for(int j = 1; j <= k; j++){ 9 if(i == 1 || j == 1){ 10 dp[i][j] = 1; 11 } 12 else if(i < j){ 13 dp[i][j] = dp[i][i]; 14 } 15 else if(i == j){ 16 dp[i][j] = dp[i][j-1]+1; 17 } 18 else{ 19 dp[i][j] = dp[i][j-1] + dp[i-j][j]; 20 } 21 } 22 } 23 } 24 int main(){ 25 int n=5,k=5; 26 memset(dp, 0, sizeof(dp)); 27 Split(n,k); 28 printf("(%d, %d)=%d",n,k,dp[n][k]); 29 return 0; 30 } 31
因为这个问题是递归的,所以可以采用递归来写,解决过程是自顶向下的。当然就在递归过程中将算过的子问题放进dp数组,在后续dp不等0时就是此前已经算过了,直接return就行。
1 #include<stdio.h> 2 #include<string.h> 3 #define MAXN 500 4 int dp[MAXN][MAXN]; 5 int dpf(int n, int k){ 6 if(dp[n][k] != 0)return dp[n][k]; 7 if(n == 1 || k == 1){ 8 dp[n][k] = 1; 9 return dp[n][k]; 10 } 11 else if(n < k){ 12 dp[n][k] = dpf(n, n); 13 return dp[n][k]; 14 } 15 else if(n == k){ 16 dp[n][k] = dpf(n, k-1)+1; 17 return dp[n][k]; 18 } 19 else{ 20 dp[n][k] = dpf(n, k-1)+dpf(n-k, k); 21 return dp[n][k]; 22 } 23 } 24 int main(){ 25 int n=5,k=5; 26 memset(dp, 0, sizeof(dp)); 27 printf("(%d, %d)=%d",n,k,dpf(n,k)); 28 return 0; 29 }