zoukankan      html  css  js  c++  java
  • 递归基础_整数划分类问题_ 多状态转移复杂递推

    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;


    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=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
    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<<"
    ";
    30     }
    31     return 0;
    32 }
    View Code

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

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

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

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

     
    对于某个数,n,可划分呢为,最多n项最少1项,那么就把划分为i项的数写为dp【i】,观察发现,

    n 的m 划分数的求法:

    1,如果分成的m 组 每个元素都不为零,那么相当于求 n-m 的m划分

    2,如果有一个组的数字为0,那么就是求n的m-1划分

     两种情况无交叉,步步递推能得到最终结果。

    这种思路类似于分苹果,代码参照这位博主 : https://blog.csdn.net/liuke19950717/article/details/51017883

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

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

    老实一点,可爱多了
  • 相关阅读:
    【HTML5 绘图与动画】使用canvas
    【H5新增元素和文档结构】新的全局属性 1. contentEditable 可编辑内容 2. contextmenu 快捷菜单 3. data 自定义属性 4. draggable 可拖动 5. dropzone 拖动数据 6. hidden 隐藏 7. spellcheck 语法检查 8. translate 可翻译
    【H5新增元素和文档结构】完善旧元素 1. a 超链接 2. ol 有序列表 3. dl 定义列表 4. cite 引用文本 5. small 小号字体 6. iframe 浮动框架 7. script 脚本
    【H5新增元素和文档结构】新的语义信息 1. address 2. time 3. figure 跟 figcaption 4. details 和 summary 5. mark 6. progress 7. meter 8. dialog 9.bdi 10. wbr 11. ruby、rt、rp 12. command
    【H5新增元素跟文档结构】新的文档结构 1. article 文章块 2. section 区块 3. nav 导航条 4. aside 辅助栏 5. main 主要区域 6. header 标题栏 7. hgroup 标题组 8. footer 页脚栏
    5_PHP数组_3_数组处理函数及其应用_9_数组集合运算函数
    【华为云技术分享】鲲鹏弹性云服务器GCC交叉编译环境搭建指南
    【华为云技术分享】7 分钟全面了解位运算
    【华为云技术分享】Linux内核编程环境 (1)
    【华为云技术分享】华为云MySQL 8.0正式商用,全新增强版开源利器强势来袭
  • 原文地址:https://www.cnblogs.com/KID-yln/p/12530936.html
Copyright © 2011-2022 走看看