1. 概述

给定一个整数n,将n拆分,问一共有多少种拆分方法。

整数拆分是很经典的问题。也是很经典的 “动态规划” 问题。

问题分析

1 = 12 = 2 = 1 + 13 = 3 = 2 + 1 = 1 + 1 + 14 = 4 = 3 + 1 = 2 + 2 = 2 + 1 + 1 = 1 + 1 + 1 + 15 = 5 = 4 + 1  = 3 + 2 = 3 + 1 + 1 = 2 + 2 + 1 = 2 + 1 + 1 + 1 = 1 + 1 + 1 + 1 + 1 6 = 6= 5 + 1= 4 + 2= 4 + 1 + 1= 3 + 3= 3 + 2 + 1= 3 + 1 + 1 + 1= 2 + 2 + 2= 2 + 2 + 1 + 1= 2 + 1 + 1 + 1 + 1= 1 + 1 + 1 + 1 + 1 + 1.......

2. 拆分方法

2.1 方法一 —— 递归法

若设f(n, m)表示将n拆分,拆分得到的序列中最大值不超过m。

递归分析

  • n == 1 或者 m == 1

    • n == 1, 即待拆分的n为1,只能拆分为 {1}
    • m == 1, 即拆分n得到的序列最大值为1,那么这个拆分得到的序列只能为{1, 1, 1, …, 1},n个1,只有这1种拆分方法;
  • 其他情况:
    • n == m 时,分为两种情况:
      a) 拆分得到的序列包含m,即序列只包含一个n —— {n},拆分结束;
      b) 拆分得到的序列不包含m,也即后面的拆分过程中拆得的最大整数只可能是m - 1,因此拆分问题可以递归到 f(n, m - 1);
      综上:n == m时的拆分方案共有:1 + f(n, m - 1)
    • n < m 时,拆分的方案共有: f(n, n)
      因为拆分不能拆分出负数出来,拆分得到的最大值只能为n,因此转移到f(n, n);
    • n > m 时
      n > m时的拆分,也分为两种情况:
      a) 拆分得到的序列包含m,那么待拆分的数就为n - m,下一次可以拆分得到的最大值仍为m,因此转移到f(n - m, m);
      b) 拆分得到的序列不包含m,那么待拆分的数还是n,下一次可以拆分得到的最大值变为了m - 1 (本次拆分不包含m,下一次也肯定就不会包含m了)

代码及测试结果

只求出整数拆分情况的总数

package cn.pku.edu.algorithm.leetcode.plus;/*** @author yumu* @date 2022/8/17*/
public class IntegerSplitCount {public int split(int n) {return helper(n, n);}private int helper(int n, int m) {if (n == 1 || m == 1) {return 1;}else {if (n == m) {return 1 + helper(n, m - 1);} else if (n < m) {return helper(n, n);} else {return helper(n - m, m) + helper(n, m - 1);}}}public static void main(String[] args) {IntegerSplitCount integerSplitCount = new IntegerSplitCount();int n = 6;int res = integerSplitCount.split(n);System.out.println(res);}
}

求出所有的整数拆分结果

import java.util.ArrayList;
import java.util.List;/*** @author yumu* @date 2022/8/16*/
public class IntegerSplit {public List<List<Integer>> split(int n) {List<List<Integer>> res = new ArrayList<>();helper(n, n, new ArrayList<>(), res);return res;}private void helper(int n, int m, List<Integer> out, List<List<Integer>> res) {if (n == 1 || m == 1) {if (n == 1) {out.add(1);res.add(new ArrayList<>(out));out.remove(out.size() - 1);} else {for (int i = 1; i <= n; i++) out.add(1);res.add(new ArrayList<>(out));for (int i = 1; i <= n; i++) out.remove(out.size() - 1);}} else {if (n == m) {out.add(m);res.add(new ArrayList<>(out));out.remove(out.size() - 1);helper(n, m - 1, out, res);} else if (n < m) {helper(n, n, out, res);} else {out.add(m);helper(n - m, m, out, res);out.remove(out.size() - 1);helper(n, m - 1, out, res);}}}public static void main(String[] args) {IntegerSplit integerSplit = new IntegerSplit();int n = 6;List<List<Integer>> res = integerSplit.split(n);for (int i = 0; i < res.size(); i++) {System.out.print(n + " = ");List<Integer> out = res.get(i);System.out.print(out.get(0));for (int j = 1; j < out.size(); j++) {System.out.print(" + " + out.get(j));}System.out.println();}}
}

得到的拆分序列如下:

2.2 方法二 —— 动态规划方法

动态规划是在上面递归思路上的优化,减少了重复计算。

dp[n][m] 表示将 n 拆分,其中拆分得的最大的数不超过 m;

状态转移分析;

  • n == 1 或者 m == 1

    1. n == 1,只能拆分为{1},因此 dp[1][m] = 1
    2. m == 1,只能拆分为{1, 1, …, 1},因此 dp[n][1] = 1
  • 其他
    • n == m 时
      拆分包含m,即只能拆分为 {m},只有这一种拆分方案;
      拆分不包含m,即之后的拆分最大能拆分得到的整数是m-1,因此转移到了 dp[n][m - 1]
      综上,n == m时的转移方程为:
      dp[n][m] = 1 + dp[n][m - 1]
    • n < m 时
      不可能拆分出负数,因此转移到 dp[n][n],所以状态转移方程为:
      dp[n][m] = dp[n][n]
    • n > m 时,分为两种情况:
      拆分包含m,转移为dp[n - m][m]
      拆分不包含m,那么后面的拆分得到的最大整数只可能是m - 1,因此转移到 dp[n][m - 1]
      综上,n < m时的转移方程为:
      dp[n][m] = dp[n - m][n] + d\[n][m - 1]

代码及测试

/*** @author yumu* @date 2022/8/17*/
public class IntegerSplitCount {public int split(int n) {// dp[n][m] 表示对整数n进行拆分,且拆分出的最大整数是m,一共的拆分方案数int[][] dp = new int[n + 1][n + 1];for (int i = 1; i < n; i++) {dp[i][1] = 1;  // 拆分得的最大整数是1,那么只能有1种拆分方案dp[1][i] = 1;  // 待拆分的整数是1,那么只能有一种拆分方案}for (int i = 1; i < n + 1; i++) {for (int j = 1; j < n + 1; j++) {if (i == 1 || j == 1) {dp[i][j] = 1;} else {if (i == j) {dp[i][j] = 1 + dp[i][j - 1];} else if (i < j) {dp[i][j] = dp[i][i];} else {dp[i][j] = dp[i - j][j] + dp[i][j - 1];}}}}return dp[n][n];}public static void main(String[] args) {IntegerSplitCount integerSplitCount = new IntegerSplitCount();int n = 6;int res = integerSplitCount.split(n);System.out.println(res);}
}

若要求出全部的拆分方案,还是只能通过递归方法获取到,动态规划只能获取到方案数。

3. 相关改编问题


从题目中可以看到,需要在完整的整数拆分中出去全部为同一个元素的情况。
因此可以首先除去拆分为全1和拆分为自身的2种情况。
然后,如果n能够被某个整数整除,也需要除去,因为n可以被拆分为全是这个元素。

代码及测试

/*** @author yumu* @date 2022/8/17*/
public class IntegerSplitPlus {public int split(int n) {if (n == 1 || n == 2) return 0;// dp[n][m] 表示对整数n进行拆分,且拆分出的最大整数是m,一共的拆分方案数int[][] dp = new int[n + 1][n + 1];for (int i = 1; i < n + 1; i++) {for (int j = 1; j < n + 1; j++) {if (i == 1 || j == 1) {dp[i][j] = 1;} else {if (i == j) {dp[i][j] = 1 + dp[i][j - 1];} else if (i < j) {dp[i][j] = dp[i][i];} else {dp[i][j] = dp[i - j][j] + dp[i][j - 1];}}}}int res = dp[n][n];res -= 2; // 除去全为1和自身的情况// 需要出去全部为同一个元素的情况// 即若n能够被一个小于它的整数整除,那么res就需要减1,因为n可以被拆分成全部为这个整数的情况int temp = (int) Math.sqrt((double) n) + 1;for (int i = 2; i <= temp; i++) {if (n % i == 0) res--;}return res;}public static void main(String[] args) {IntegerSplitPlus integerSplitPlus = new IntegerSplitPlus();for (int n = 1; n <= 6; n++) {int res = integerSplitPlus.split(n);System.out.println(res);}}
}

可以看到,拆分方案数与题目显示的一致。

【算法】整数拆分问题相关推荐

  1. python整数拆分dp算法_整数拆分问题的四种解法【转载】

    http://blog.csdn.net/u011889952/article/details/44813593 整数拆分问题的四种解法 原创 2015年04月01日 21:17:09 整数划分问题是 ...

  2. 试题 算法训练 整数拆分

    试题 算法训练 整数拆分 资源限制 内存限制:256.0MB C/C++时间限制:1.0s Java时间限制:3.0s Python时间限制:5.0s 问题描述 对于给定的正整数S,将其拆分为正整数的 ...

  3. 整数拆分问题的四种解法

    整数划分问题是算法中的一个经典命题之一 所谓整数划分,是指把一个正整数n写成如下形式: n=m1+m2+m3+....+mi;(其中mi为正整数,并且1<=mi<=n),则{m1,m2,m ...

  4. 整数拆分的两种解法(已完成)

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! 整数拆分 ...

  5. C++ 如何将一个大的整数 拆分0到9单个数字

    如何将一个大的整数拆分成单个整数 第一种解决方案 第二种解决方案 分享思路 希望能帮到你 第一种解决方案 纯算法的方式 //完整数int value = 123456;//拆分后的个位数int sub ...

  6. python 整数 拆分 分段

    整数 拆分 分段 不平均分 int1 = 68 # 整数 sub = 3 # 要分成几段 interval = int1 // sub rem = int1 % sub print("余数: ...

  7. LeetCode-动态规划基础题-343. 整数拆分

    描述 343. 整数拆分 给定一个正整数 n,将其拆分为至少两个正整数的和,并使这些整数的乘积最大化. 返回你可以获得的最大乘积. 示例 1: 输入: 2 输出: 1 解释: 2 = 1 + 1, 1 ...

  8. python分割数字_python实现整数拆分,输出拆分序列

    昨天笔试VIPKID有一道关于整数拆分的题目,要求输出拆分后的序列,当时没有做出来,记录一下可以实现的想法: 题目示例: 从键盘读入一个数 n, 输出所有和为 n 的子序列和,包括 n 测试用例: 输 ...

  9. leetcode 343. Integer Break | 343. 整数拆分(Java)

    题目 https://leetcode.com/problems/integer-break/ 题解 一开始以为,只要是 3 个数相加就可以,于是在错误的方向上越走越远: 后来发现并不只是 3 个数, ...

最新文章

  1. View_01_LayoutInflater的原理、使用方法
  2. 操作系统中的处理机调度调度_操作系统中的流程分类和调度
  3. 工作341:uni-表头不显示
  4. Maven:maven-shade-plugin, 打包失败, MojoExecutionException: Error creating shaded jar: null
  5. 【uoj#180】[UR #12]实验室外的攻防战 结论题+树状数组
  6. C# 利用ICSharpCode.SharpZipLib.dll 实现压缩和解压缩文件
  7. Codeforce 1600Difficulty Graphs 20 questions
  8. 小程序中的api接口请求封装
  9. 微信翻译生日快乐的代码_微信表情翻译表白代码彩蛋怎么设置! 表情emm加符号翻译文字暗语大全...
  10. 2017阿里巴巴实习生C/C++研发内推一面、二面经历
  11. 【MIUI9】小米平板1MIPAD1欧版ROM历史ROM下载地址-另附挥泪典藏版V9系统
  12. 快让你的App分20亿吧!
  13. 谈谈DRAM的检测等级(eTT与uTT)
  14. 使用EasyExcel完成导出导入功能
  15. 人眼特征标定数据(睁闭眼+瞳孔位置)
  16. DRAM知识整理系列(一):SDRAM的简介与SDRAM的管脚与尺寸介绍
  17. 欢迎前来日本王子酒店享用当地应季美食
  18. 昆明世博园装mysql_昆明世博园太美丽了
  19. 【域名】域名解析优先级以及其他玩法
  20. 高通导航器软件开发包使用指南(13)

热门文章

  1. KDD 2021 | 基于多智能体协同竞价博弈的电商搜索广告多目标竞价优化
  2. 慢牛股票-基于Sencha touch+Cordova的股票类APP
  3. Android+上百实例源码分析以及开源分析+集合打包
  4. 自己做量化交易软件(12)通通量化AI框架的使用1-与通通AI对话聊天
  5. html及Dreamweaver学习心得
  6. Geoserver2.11矢量切片与OL3中的调用展示
  7. 【阅读笔记】Cost Volume Pyramid Based Depth Inference for Multi-View Stereo
  8. vmware workstation虚拟机安装Debian系统教程
  9. php获得当前时间差,PHP获取当前时间差8小时的问题
  10. 蓝桥杯2014省赛——猜年龄(Java)