打印正整数n拆分的所有情况

2023-05-16

题目:把一个正整数n拆分成若干个正整数的相加,列出所有组合 例如:
4=4
4=1+3=2+2
4=1+1+2
4=1+1+1+1
动机:网上有好多解答,大部分都是给出拆分结果的个数,不能把把每一种的情况打印出来,或者效率低,本人接触这个题目很久了,最近心血来潮想搞定它,嘿嘿

思路一:
设置一个递归方法recursive(int last, int curSum, int n),该方法包含以下几个步骤:
0. 递归出口: 当 last+curSum > n ,返回
1. 该递归方法包含三个参数
a. last:上个递归传递给它的起始数字
b.curSum:当前累加和
c. n:待拆分的数字
2. 设置变量i从last开始到n遍历:
a. curSum=curSum+i;
b. list(成员变量)加入i
c. 判断curSum==n ,如果成立,打印list
3. 递归调用 recursive(i, curSum,n);
4. 还原curSum,list,消除下一层递归对本层递归在成员变量curSum,list的影响。

实现代码
package com.uestc.miaoshi;

import java.util.ArrayList;
import java.util.Scanner;
public class BreakN2 {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        while (in.hasNextInt()) {
            int n = in.nextInt();
            count = 0;
            recursive(1, 0 , n);
            System.out.println("所有组合总数" + count);
        }
    }

   static int count = 0;
   static ArrayList<Integer> list = new ArrayList<>();
   static void recursive(int last, int curSum, int n) {
        if (last + curSum > n) return;

        for (int i = last; i <= n ; i++) {
            curSum = curSum + i;
            list.add(i);
            if (curSum == n) {
                count ++;
                System.out.println(list);
            }
            recursive(i, curSum, n);
            curSum = curSum - i;
            Integer o = i;
            list.remove(o);
        }
    }
}


输入 5 ,打印出以下结果:
在这里插入图片描述 这种思路的优点是容易思考,缺点是当n很大时,耗时大,归根是算法效率低。下面我们继续介绍一种效率高的解决方法。
思路二:动态规划,设置一个函数 f(n, m) ,该函数的功能就是把正整数分成 m等分, 并且m 份中不允许为0,也就是说每一份至少大于等于1. 我们可以得到状态转移方程:
f(n ,m ) = f(n-m,m) + f(n-1, m-1)
看到这里如果,你能经过自己的思考就明白这个公式,我只能膜拜,因为你的天赋很高,哈哈,好了,言归正传。
方程的第一项 f(n-m, m) 代表m份全部都大于1的分法,也可以这样理解,先把 m 拆分成 m 个 1 ,每一份都是1 ,然后 再把 n - m 分到 m 份中
方程的第二项是 f(n-1, m-1)代表至少存在一份为1的情况,那么怎么保证呢?先从n中拿出1,放到m份中的1份,然后把 n-1分到 m-1份
总结: f(n,m)=f(n-m, m)不包含1的组合+f(n-1, m-1)包含1的组合,一分为二的思想
实现代码:

package com.uestc.miaoshi;
import java.util.Scanner;
public class BreakN {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        while (sc.hasNextInt()) {
            long start = System.currentTimeMillis();
            int res = 0;
           int n = sc.nextInt();
            for (int i = 1; i <= n; i++) {
                res = res + f(n, i);
            }
            System.out.println("总共组合数: " + res);
            long end = System.currentTimeMillis();
            System.out.println("耗时秒数:"+ (end - start)/1000);
        }
    }
    /**
     * 函数功能,把正整数 n 分成 m 等份  4 = 1+3 = 2+2
     * @param n
     * @param m
     *  f(n) = f(n-m, m) + f(n-1, m-1)
     */
    private static int f(int n, int m) {
        if ( n <=0 || m <= 0 || n < m) {
            return 0;
        }
        if (m == 1) return 1;
        return f(n - m, m) + f(n-1, m-1);
    }
}

其实说到这里我们只是把问题解决了一半,我们可以轻松的得出所有组合的总数,我们还不能把每一种组合的具体情况打印出来。咋办呢,山人自有妙计。
思路我就照着下面这个图讲吧
在这里插入图片描述首先定义树的数据结构:

class TreeNode {
    int n, m;
    ArrayList<Integer> res = new ArrayList<>();
    boolean addAll;
    TreeNode left, right, parent;

    public TreeNode(int n, int m, boolean addAll) {
        this.n = n;
        this.m = m;
        this.addAll = addAll;
    }
}

addAll =false表示f(n-1, m-1), 也就是代表m份包含1的分法,由下而上回溯时,上结点的res=下结点的res加上一个元素1
addAll =true表示f(n-m, m), 也就是代表m份不包含1的组合的分法,由下而上回溯时,上结点的res=下结点的res每个元素加上1
res表示具体的分法
我们可以把图看成是二叉树的升级版本(每一个节点都有父指针),叶子节点表示具体的分法,比如f(1,1) = 1 ,f(3,1)=3。
我们可以从叶子结点回溯到根结点,例如
f(1,1) = 1, addAll=false,向上回溯到f(2,2)结点,导致f(2,2)结点的res =[1,1]
f(2,2),res=[1,1],addAll=true,向上回溯到f(4,2)结点,导致f(4,2)结点的res=[2,2]
f(3,1),res=[3],addAll=false,向上回溯到f(4,2)结点,导致f(4,2)结点的res=[1,3]

下面看具体实现代码:

package com.uestc.miaoshi;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Scanner;

class TreeNode {
    int n, m;
    ArrayList<Integer> res = new ArrayList<>();
    boolean addAll;
    TreeNode left, right, parent;
    public TreeNode(int n, int m, boolean addAll) {
        this.n = n;
        this.m = m;
        this.addAll = addAll;
    }
}
public class BreakNToM {
    static ArrayList<TreeNode> leafs = new ArrayList<>();
    static ArrayList<ArrayList<Integer>> result = new ArrayList<>();

    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        while (in.hasNextInt()) {
            long start = System.currentTimeMillis();
            int n = in.nextInt();
            int m = in.nextInt();
            leafs.clear();
            f(n, m, true);

            for (TreeNode leaf : leafs) {
                recursive(leaf);
            }
            long end = System.currentTimeMillis();
            System.out.println( (end - start)/1000);

            System.out.println(result);
        }
    }
    private static void recursive(TreeNode leaf) {
        if (leaf.parent == null) {
            Collections.sort(leaf.res);
            result.add(leaf.res);
            return;
        }
        ArrayList<Integer> res = leaf.res;
        if (leaf.addAll) {
            ArrayList<Integer> temp = new ArrayList<>();
            for (int x : res) {
                temp.add(x + 1);
            }
            leaf.parent.res = temp;
        } else {
            res.add(1);
            leaf.parent.res = res;
        }
        recursive(leaf.parent);
    }

    static TreeNode f(int n, int m, boolean allAdd) {
        if (n < m || n <= 0 || m <= 0) {
            return null;
        }
        TreeNode node = new TreeNode(n, m, allAdd);
        if (n == m) {
            TreeNode leaf = new TreeNode(n, n, allAdd);
            for (int i = 0; i < m; i++) {
                leaf.res.add(1);
            }
            leafs.add(leaf);
            return leaf;
        }
        if (m == 1) {
            TreeNode leaf = new TreeNode(n, 1, allAdd);
            leaf.res.add(n);
            leafs.add(leaf);
            return leaf;
        }

        node.left = f(n - m, m, true);
        if (node.left != null)
            node.left.parent = node;
        node.right = f(n - 1, m - 1, false);
        if (node.right != null)
            node.right.parent = node;
        return node;
    }
}



 输入 8  3  

在这里插入图片描述
搞定

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

打印正整数n拆分的所有情况 的相关文章

  • Ubuntu使用root登录系统界面、免密码、添加开机启动

    以下测试环境为Ubuntu 16 04 一 使用root登录系统界面 Ubuntu在命令行模式下 xff0c 是可以登录root的 为了安全 xff0c 在图形界面模式下 xff0c 默认不允许使用root登录系统 可以通过修改50 uni
  • 线程(Thread)的三种等待唤醒机制详解

    1 为什么需要线程的等待和唤醒 线程的等待唤醒机制是一种经典的 生产者和消费者 模型 例如食品加工厂 xff0c 食品加工人员和原料补给人员 xff0c 在有充足原料时 xff0c 补给人员是在等待 xff0c 等到原料不够时 xff0c
  • Linux常用终端命令之cat、grep、echo

    这三个指令 xff0c 每一个都很常用 xff0c 用法也都很多 作为一个linux初学者 xff0c 我还不能很好的掌握三个命令的用法 xff0c 于是先在这篇博客里做一个简单的整理和总结 xff0c 以加深对三个指令的理解 grep 先
  • Python+Pycharm使用opencv

    http blog csdn net whykifan article details 66478421 在这个配置过程中 xff0c 遇到了不少的问题 xff0c 于是就写了这篇博文 xff0c 希望可以帮到遇到相同问题的人 主要步骤 x
  • Linux启动流程rcN.d rcS.d rc.local等

    1 环境 当前系统环境为 xff1a Linux mint mate 17 1 基于ubuntu14 04的衍生版 备注 xff1a etc rc d文件夹中的脚本文件的链接目标为 xff1a etc init d文件夹下的脚本 为系统运行
  • Ubuntu 启用root账户并开启远程登录

    生产环境尽量不要如下操作 生产环境尽量不要如下操作 生产环境尽量不要如下操作 本地虚拟机 xff0c 为了方便 xff0c 直接root远程登录 Ubuntu 20 4 1 启用root账户 sudo passwd root 2 开启远程登
  • datatable中报错 table.XXX is not a function的解决方法

    在datatable中可以通过三种不同的方式为一个或多个表获取一个新的Datatables API实例 xff1a xff08 1 xff09 selector DataTable xff08 2 xff09 selector dataTa
  • 在ubuntu下安装libpcap库

    这两天公司里要我了解一下pcap xff0c 但是还不知道它是干什么的 首先 xff0c 我从网上查到了 xff0c pcap实际上是抓包库 这个抓包库给抓包系统提供了一个高层次的接口 所有网络上的数据包 xff0c 甚至是那些发送给其他主
  • Python 获取当前路径(文件及所在文件夹,正斜线)

    参考博客 xff1a http www cnblogs com wind wang p 5822192 html 更多路径读取请参照上述博客 xff08 使用Python 2 x版本 xff09 xff0c 这里只挑出个人认为最直接 常用的
  • 基于docker搭建mysql,redis,mongo,gitlab,wiki等开发环境

    今天基于docker搭建一整套开发环境 首先安装docker yum y install docker io 使用加速器 xff1a vim etc docker daemon json 添加163的加速地址 xff1a 34 regist
  • Android编译系统分析五:system.img的生成过程

    Android编译系统分析系列文章 xff1a android编译系统分析 xff08 一 xff09 source build envsetup sh与lunch Android编译系统 xff08 二 xff09 mm编译单个模块 an
  • linux学习笔记--Xshell远程登陆linux

    1 登录xshell官网XSHELL NetSarang Website xff0c 输入姓名和邮箱 xff0c 下载免费版的Xshell xff0c 下载链接会发送到邮箱中 下载完成后直接安装即可 2 双击打开安装完成的Xshell xf
  • Apache使用localhost可以访问但使用本机IP(局域网)不能访问

    Apache使用localhost可以访问但使用本机IP 局域网 不能访问 Apache 使用localhost 127 0 0 1 可以访问 xff0c 使用本机IP 局域网 不能访问 xff0c 为什么本机ip地址不能访问localho
  • ubuntu14.04LTS命令行安装xfce4桌面

    安装ubuntu14 04LTS后 xff0c 需要一个桌面 xff0c 因此选择安装xfce4 1 安装 1 1 安装默认版本4 10 sudo apt get install xfce4 1 2 安装版本4 12 1 sudo add
  • OCLint的部分规则(Convention 部分)

    OCLint的部分规则 xff08 Convention 部分 xff09 对OCLint的部分规则进行简单翻译解释 xff0c 有部分进行了验证以及进一步分析 测试 OCLint其他相关内容如下 xff1a OCLint iOS OC项目
  • 修改virualbox下的虚拟系统Ubuntu的内存

    VBoxManage exe在VirtualBox 安装目录下 xff0c 如下图 xff0c 我们进VirtualBox 安装目录查看到VBoxManage exe 要使用这个工具 xff0c 就先了解一下这个工具吧 xff0c 要用命令
  • 四旋翼定高篇之惯导加速度+速度+位置三阶互补融合方案

    笔者最近正在做四旋翼惯性导航的部分 xff0c 利用加速度计进行速度估计 位置估计 xff0c 从而实现四旋翼的垂直方向上的定高 水平方向上的定点控制 首先在这里引用学长之前参考APM飞控里面互补滤波惯导融合方案 xff1a 原文见四旋翼位
  • 从APM源码分析GPS、气压计惯导融合

    最近事多 xff0c 忙着开源自研飞控 xff0c 现主要工作基本已经完成 xff0c 代码最迟下月中旬开放 xff0c 博客来不及更新 xff0c 还请各位见谅 xff0c 后面会抽空多更的咯 xff01 xff01 xff01 自研飞控
  • Angular 展示标签内容,而不是标签本身

    在angualr项目的html页面中 xff0c 如果展示的数据包含html标签 xff0c 则不会被解析 xff0c 而是原样展示标签 xff0c 比如 lt br gt xff0c 而不会产生换行的效果 xff0c 解决办法是 xff0

随机推荐