
[编程题]: 整数拆分问题
发布日期:2021-05-06 22:54:25
浏览次数:31
分类:精选文章
本文共 4079 字,大约阅读时间需要 13 分钟。
[编程题]: 整数拆分问题
给定一个整数n,将n拆分,问一共有多少种拆分方法。
整数拆分是很经典的问题。也是很经典的 “动态规划” 问题。
问题分析
1 = 12 = 2 = 1 + 1 3 = 3 = 2 + 1 = 1 + 1 + 1 4 = 4 = 3 + 1 = 2 + 2 = 2 + 1 + 1 = 1 + 1 + 1 + 1 + 1 5 = 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.......
1. 递归方法
若设f(n, m)表示将n拆分,拆分得到的序列中最大值不超过m.
求解f(n, m)分为如下几种情况:
- n == 1 或者 m == 1
- n == 1, 即待拆分的n为1,只能拆分为 {1}
- m == 1, 即拆分n得到的序列最大值为1,那么这个拆分得到的序列只能为{1, 1, 1, …, 1},n个1
- 其他情况:
- n == m n == m时,分为两种情况: 1.1 拆分得到的序列包含m,即序列只包含一个n —— {n},拆分结束 1.2 拆分得到的序列不包含m,即序列{m, {…}},后面的拆分序列为子问题 f(n - m, m) 综上:n == m时的拆分方案共有:1 + f(n - m, m)
- n < m n < m时,拆分的方案共有: f(n, n) 因为 拆分不能拆分出负数出来,拆分得到的最大值只能为n,因此转移到f(n, n).
- n > m n > m时的拆分,也分为两种情况: 3.1 拆分得到的序列包含m,那么待拆分的数就为n - m,下一次可以拆分得到的最大值仍为m,因此转移到f(n - m, m); 3.2 拆分得到的序列不包含m,那么待拆分的数还是n,下一次可以拆分得到的最大值变为了m - 1 (本次拆分不包含m,下一次也肯定就不会包含m了)
代码
public static int f(int n, int m) { if (n == 1 || m == 1) { return 1; } else { if (n == m) { return 1 + f(n, m - 1); } else if (n < m) { return f(n, n); } else { return f(n - m, m) + f(n, m - 1); } }}
如果要得到所有的拆分序列,则在拆分过程中保存下来拆分结果。
public static int f(int n, int m, Listone, List
> res) { if (n == 1 || m == 1) { if (n == 1) { one.add(1); res.add(new ArrayList<>(one)); one.remove(one.size() - 1); } else { for (int i = 1; i <= n; i++) one.add(1); res.add(new ArrayList<>(one)); for (int i = 1; i <= n; i++) one.remove(one.size() - 1); } return 1; } else { if (n == m) { one.add(m); res.add(new ArrayList<>(one)); one.remove(one.size() - 1); return 1 + f(n, m - 1, one, res); } else if (n < m) { return f(n, n, one, res); } else { one.add(m); int ans1 = f(n - m, m, one, res); one.remove(one.size() - 1); int ans2 = f(n, m - 1, one, res); return ans1 + ans2; } }}
得到的拆分序列如下:

2. 动态规划方法
动态规划是在上面递归思路上的优化,减少了重复计算。
设 dp[n][m] 表示将 n 拆分,其中拆分得的最大的数不超过 m ;
跟上面一样的情况:- n == 1 或者 m == 1
- n == 1,只能拆分为{1}
- m == 1,只能拆分为{1, 1, …, 1}
- 其他
- n == m 拆分包含m,即只能拆分为: {n};拆分结束 拆分不包含m,即转移为 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,转移为dp[n][m - 1]. 综上,n < m时的转移方程为: dp[n][m] = dp[n - m][n] + dp[n][m - 1]
代码
public class Main { public static void main(String[] args) { Scanner input = new Scanner(System.in); while (input.hasNext()) { int n = input.nextInt(); 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] = dp[i][j - 1] + 1; } else if (i < j) { dp[i][j] = dp[i][i]; } else { dp[i][j] = dp[i - j][j] + dp[i][j - 1]; } } System.out.print(dp[i][j] + " "); } System.out.println(); } System.out.println("res = " + dp[n][n]); } }}
相关的改编
- n == 1 或者 m == 1
- n == 1,只能拆分为{1}
- m == 1,只能拆分为{1, 1, …, 1}
- 其他
- n == m 拆分不能包含n自身,因此这里的拆分必须转移到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,转移为dp[n][m - 1]. 综上,n < m时的转移方程为: dp[n][m] = dp[n - m][n] + dp[n][m - 1]
发表评论
最新留言
哈哈,博客排版真的漂亮呢~
[***.90.31.176]2025年03月31日 01时46分29秒
关于作者

喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
快速学习汇编之 常见汇编指令
2019-03-05
变量覆盖漏洞
2019-03-05
java 之 集合篇
2019-03-05
weblogic之cve-2015-4852
2019-03-05
Java注释
2019-03-05
水调歌头·1024
2019-03-05
对不起
2019-03-05
C++ 函数默认参数
2019-03-05
C++ 函数重载
2019-03-05
C++ sort()函数使用简介
2019-03-05
Kickdown UVA - 1588
2019-03-05
matlab文件管理
2019-03-05
Printer Queue UVA - 12100
2019-03-05
【并发编程】实现多线程的几种方式
2019-03-05
设计模式系列博客传送门
2019-03-05
设计模式——访问者模式
2019-03-05
同步锁 —— ReentrantReadWriteLock
2019-03-05
Nginx简介
2019-03-05
Nginx的Gzip功能
2019-03-05
当我们开发一个接口时需要注意些什么
2019-03-05