递归基础_整数划分类问题_ 多状态转移复杂递推& 记忆化搜索

E: 整数划分问题题目描述 

将正整数 n 表示成一系列正整数之和, n = n 1 + n 2 + . . . + n k ,其中 n 1 ≥ n 2 ≥ n 3 ≥ . . . ≥ n k ≥ 1 , k ≥ 1 。
正整数 n 的这种表示称为正整数 n 的划分,正整数 n 的不同的划分个数称为正整数 n 的划分数,记作 p ( n ) 。
例如,正整数 6 有如下 11 种不同的划分,所以 p ( 6 ) = 11 。

6 = 6;
6 = 5 + 1;
6 = 4 + 2;
6 = 4 + 1 + 1;
6 = 3 + 3;
6 = 3 + 2 + 1;
6 = 3 + 1 + 1 + 1;
6 = 2 + 2 + 2;
6 = 2 + 2 + 1 + 1;
6 = 2 + 1 + 1 + 1 + 1;
6 = 1 + 1 + 1 + 1 + 1 + 1;

输入描述

多组数据,每组数据包含一个正整数 n ( 1 ≤ n ≤ 20 ) 。

输出描述

每组数据输出一行,包含一个正整数 k ,表示正整数 n 的划分数。

样例输入

1
2

样例输出

1
2

题解:

这题课上,老师用的办法就是递归,而且和汉诺塔不一样,它的状态转移方程除了递归“整体法”,分步可以很容易表示出来,这类递归已经几乎就是递推来实现,

直接找规律找不到sum的规律,而且设置sum作为变量来做,状态方程更加麻烦,会有重复筛选的情况,所以用模拟的办法

6 = 6;


6 = 5   + 1;  (dp【1】【j】)


6 = 4   + 2;
6 = 4   + 1   + 1; (dp【2】【j】)


6 = 3   + 3;
6 = 3   + 2   + 1;
6 = 3   + 1   + 1   + 1; ( dp【3】【j】)


6 = 2   + 2   + 2;
6 = 2   + 2   + 1   + 1;
6 = 2   + 1   + 1   + 1   + 1;   (dp【4】【j】-  k)


6 = 1   + 1   + 1   + 1   + 1   + 1; (dp【5】【j】-k)

除了多个状态要考虑,这题找规律过程最重要的是发现它是按照什么方式往后面寻找,从而使得情况不会有重复的出现 

而且题目的 n=6 例子给了提示,即从5—1,而后面的数必定小于等于前一个数(an >= an+1),极限是最终分解为 n个数 

这个是例题,先给出例题的解法—不是直接模拟,而是模拟了搜索树,

但是它不需要遍历到最后,然后把所有叶子节点都记下,

它的办法是计分支数,每次遇到有分支就+1,然后递归下一步,把之后的分支数的返回加起来

—题解的状态转移方程,直接设为二元变量,f(a,b),输出对象结果序列的次数,规定a小于等于b为标准情况

f(n, m) = 1;                      ( n = 1 or m = 1 )

f(n, n);                        ( n < m )//分割n本身

1+ f(n, m - 1);                  ( n = m )//需要有-1操作才能有变化

f(n - m, m) + f(n, m - 1);   ( n > m )//

要求

 先确定自变量,然后对—拆数方法—分类讨论,拆数的依据是m与n的大小关系_可分为,

  • 最终恰好有一个为1,此时找到一个分支
  • n=m,但不到1,没到底,令其中一个区间收缩
  • 一个是1 ,一个是n-1或m-1,
  • 以及最难想的一种情况4 ,一个是m和n-m,刚好互补的关系,然后,另一个n不变,m变

 在递归的过程中,不断分解成两块,但是它是两个变量都是自动处理的,

有点类似于”二分搜索“—有一点分治的意思在里面,(这个概念现在还不是很熟练,以后另外补上)

代码:

 1 #include <iostream>
 2 #include <cstring>
 3 #include <queue>
 4 #include <cstdio>
 5 #include <cmath>
 6 #include <map>
 7 #include <algorithm>
 8 typedef long long ll;
 9 using namespace std;
10 int n1;
11 int sum=0;
12 
13 int f(int m,int n) {
14     if(m==1||n==1) {
15         return 1;
16     }
17     if(m==n) {
18         //此时需要有一个做变动才行
19         return f(m,m-1)+1;
20 
21     }
22     if(m<n) {
23         return f(m,m);
24 
25     }
26     if(m>n) {
27         return f(m-n,n)+f(m,n-1);
28     }
29 
30 
31 }
32 
33 
34 
35 int main() {
36 
37     while(cin>>n1) {
38         cout<<f(n1,n1)<<"\n";   //最深不会超过n
39     }
40 
41 
42 
43 
44     return 0;
45 
46 }
View Code

然后这是我没做出来的思路,如果不看题解,规定递归做,一开始会这样想,每次固定 a :

设   f(a,n)为分解 n 最终所得序列(这个序列整体作为一个状态,或者说自变量)n表示最终最多分解成n个1;

f(a,n)= { n:(n=a) || a-1 + f(n-a+1):(a=n-1,n-2,n-3,····,1),(要求,a >=   f(n-a)中的任意元素)} 

相当于a1 从 n一直试到 1 ,a1固定,拆n-a1,此时再固定a2,拆n-a1-a2,直到n-a1-a2-an,试到1 返回

然而 并。没。有。做出来。

目前反思:因为这里设置的边界条件是模糊的,a=1  并不清晰,因为这个条件不能保证求和的值 sum== n

做不出来的原因可能是,既想着模拟分割来做,又想着—搜索(第三步相当于dfs)来做,思路混乱,也不是很了解混用的流程,返回先决条件设置错误。,

那还做不出来怎么办,这个题显然可以用记忆化搜索查完,和刚刚那个想法类似,写了一个dfs的方式,

之前分割方式是否到底返回先决条件用和是否为 n 来判断

但是判断条件改为,令和为sum自动弹出当前stack,sum-stack,回到上一层,

 1 #include <stdio.h>
 2 #include <iostream>
 3 # define MAXN 100
 4 using namespace std;
 5 int mark[256];
 6 int n;
 7 int len=0;
 8 int sum=0;
 9 void DFS(int k, int pr) {
10     if(sum > n) {
11         return;
12     } else if(sum == n) {
13         len++;
14 
15     } else {
16         for(int j = pr; j > 0; j--) {
17             mark[k] = j;//赋值是直接覆盖不用弹出
18             sum += j;
19             DFS(k+1,j);
20             sum -= j;
21         }
22     }
23 }
24 int main() {
25     while(cin>>n) {
26         sum=0;
27         len=0;
28         DFS(0,n);
29         cout<<len<<"\n";
30     }
31     return 0;
32 }
View Code

至于bfs实现,个人理解,目前题目要求已经限制了宽度,检查需要的深度=1(不用递归到最后就可以了),宽度遍历不存在优势,也不需要剪枝

dp的思路——基于目前我对dp的理解程度

般是利用二维数组,将搜索树的问题转化到二维数组——二元变量的关系上,一般难点再找关系和定义dp【i】【j】上,再找递推式

本题要求输入 n 的划分总数,容易想到,容易想到,大的划分可以分解成小的划分,总数也可以由此加上去,但是注意不能重复,

dp【i】可定义为 ,整数 为 i 时划分总数 

6 = 6;

6 = 5   + 1;  (dp【1】=1)

6 = 4   + 2;
6 = 4   + 1   + 1; (dp【2】=2)

6 = 3   + 3;
6 = 3   + 2   + 1;
6 = 3   + 1   + 1   + 1; ( dp【3】=3)

6 = 2   + 2   + 2;
6 = 2   + 2   + 1   + 1;

6 = 2   + 1   + 1   + 1   + 1;   (dp【4】-  k = 3 )

6 = 1   + 1   + 1   + 1   + 1   + 1; (dp【5】-   k = 1)

还有另外一种,利用二维数组的dp方法: https://blog.csdn.net/qq_31975227/article/details/68191053

这里第二种不是很明白,回头再看看吧,想起来不容易,记忆化搜索更加实用。

猜你喜欢

转载自www.cnblogs.com/KID-yln/p/12530936.html