标签:style blog http io color os ar for sp
此题是个非常经典的题目,这个题目包含了整数划分(一)和整数划分(二)的所有情形,而且还增加了其它的情形,主要是用递归或者说是递推式来解,只要找到了递推式剩下的任务就是找边界条件了,我觉得边界也是非常重要的一步,如果找不准边界,这个题也很难做出来,当时我就是找边界找了好长时间,边界得琢磨琢磨。递推步骤如下:
第一行:将n划分成若干正整数之和的划分数。
状态转移方程:dp[i][j]:和为i、最大数不超过j的拆分数
dp[i][j]可以分为两种情况:1、拆分项至少有一个j 2、拆分项一个j也没有
dp[i][j] = dp[i-j][[j] + dp[i][j-1]
第二行:将n划分成k个正整数之和的划分数。
dp[n-k][k]:相当于把k个1从n中拿出来,然后和n-k的拆分项相加的个数
第三行:将n划分成若干最大不超过k的正整数之和的划分数。
dp[n][k]
第四行:将n划分成若干奇正整数之和的划分数。
dp1[i][j]是当前的划分数为i,最大值为j时的中的划分数,则状态转移方程为
if(i < j && j % 2 == 1)
dp1[i][j] = dp1[i][i]
if(i < j && j % 2 == 0) (最大数不可能为偶数)
dp1[i][j] = dp1[i][i-1]
划分数中有j时的划分为dp[i][j - 2],因为它是奇数,所以要减2,
如果划分数中没有j的时候, 则它的数目可以写成dp1[i-j][j];意思就是i去掉j后,然后再划分最大为j的
即dp1[i][j] = dp1[i-j][j] + dp1[i][j-2]
第五行:将n划分成若干完全不同正整数之和的划分数。
dp2[i][j]可以分两种情况:1、dp1[i][j-1]为不选择j时的方案 2、dp1[i-j][j-1]为选择j时的方案
0-1背包:dp2[i][j] = dp2[i][j-1] + dp2[i-j][j-1]
方法一(递归法):
1 #include <stdio.h> 2 //less_m(n, m)表示将n划分为最大是m的数 3 int less_m(int n, int m) 4 { 5 if(n == 1 || m == 1) 6 return 1; 7 if(n < m) 8 return less_m(n, n); 9 else if(n == m) 10 return 1 + less_m(n, m - 1); 11 else 12 return less_m(n, m - 1) + less_m(n - m, m); 13 } 14 //count(n, m)表示将n划分为m个数 15 int count(int n, int m) 16 { 17 if(n < m) 18 return 0; 19 if(m == 1 || n == m) 20 return 1; 21 else 22 return count(n - 1, m - 1) + count(n - m, m); 23 } 24 //odd(n, m)表示将n划分为最大数为m的奇数之和 25 int odd(int n, int m) 26 { 27 if(m == 1) 28 return 1; 29 if(n == 0 && m % 2 == 1) 30 return 1; 31 if(n == 0 && m == 0) 32 return 1; 33 if(n < m) 34 { 35 if(n % 2 == 0) 36 return odd(n, n - 1); 37 else 38 return odd(n, n); 39 } 40 else 41 { 42 return odd(n - m, m) + odd(n, m - 2); 43 } 44 45 } 46 //not_duplicate(n, m)是将n划分为最大为m的数, 并且没有重复的 47 int not_duplicate(int n, int m) 48 { 49 if(m == 0) 50 return 0; 51 if(n == 1 || n == 0) 52 return 1; 53 if(n < m) 54 return not_duplicate(n, n); 55 else 56 return not_duplicate(n, m - 1) + not_duplicate(n - m, m - 1); 57 } 58 59 int main() 60 { 61 int n, k; 62 while(~scanf("%d %d", &n, &k)) 63 { 64 printf("%d\n", less_m(n, n)); 65 printf("%d\n", count(n, k)); 66 printf("%d\n", less_m(n, k)); 67 if(n % 2 == 1) 68 printf("%d\n", odd(n, n)); 69 else 70 printf("%d\n", odd(n, n - 1)); 71 printf("%d\n\n", not_duplicate(n, n)); 72 } 73 74 return 0; 75 }
方法二(递推法dp):
1 #include <stdio.h> 2 const int MAX = 52; 3 int dp[MAX][MAX], dp1[MAX][MAX], dp2[MAX][MAX]; 4 void divide() 5 { 6 dp[0][0] = 1; 7 for(int i = 0; i < MAX; i++) 8 { 9 for(int j = 1; j < MAX; j++) 10 { 11 if(i < j) 12 dp[i][j] = dp[i][i]; 13 else 14 dp[i][j] = dp[i][j - 1] + dp[i - j][j]; 15 } 16 } 17 } 18 //这是划分奇数的函数 19 void divide1() 20 { 21 for(int i = 1; i < MAX; i++) 22 dp1[i][1] = 1; 23 for(int i = 1; i < MAX; i += 2) 24 dp1[0][i] = 1; 25 dp1[0][0] = 1; 26 for(int i = 1; i < MAX; i++) 27 { 28 for(int j = 3; j < MAX; j += 2) 29 { 30 if(i < j) 31 { 32 if(i % 2 == 1) 33 dp1[i][j] = dp1[i][i]; 34 else 35 dp1[i][j] = dp1[i][i - 1]; 36 } 37 else 38 dp1[i][j] = dp1[i][j - 2] + dp1[i - j][j]; 39 } 40 } 41 } 42 //划分没有重复数字的函数 43 void divide2() 44 { 45 for(int i = 1; i < MAX; i++) 46 dp2[0][i] = dp2[1][i] = 1; 47 for(int i = 2; i < MAX; i++) 48 { 49 for(int j = 1; j < MAX; j++) 50 { 51 if(i < j) 52 dp2[i][j] = dp2[i][i]; 53 else 54 dp2[i][j] = dp2[i][j - 1] + dp2[i - j][j - 1]; 55 } 56 } 57 } 58 59 int main() 60 { 61 int n, k; 62 divide(); 63 divide1(); 64 divide2(); 65 while(~scanf("%d %d", &n, &k)) 66 { 67 printf("%d\n", dp[n][n]); 68 printf("%d\n", dp[n - k][k]); 69 printf("%d\n", dp[n][k]); 70 //先要判断要划分的数是否是奇数 71 printf("%d\n", (n & 1) ? dp1[n][n] : dp1[n][n - 1]); 72 printf("%d\n\n", dp2[n][n]); 73 } 74 75 }
标签:style blog http io color os ar for sp
原文地址:http://www.cnblogs.com/Howe-Young/p/4066049.html