整数划分
转载及参考声明
本文大部分转载自CSDN-@tyilack_小小黑
另有菜鸟教程
其他参考:
https://blog.csdn.net/li_ya_fei/article/details/84725057
https://iowiki.com/java/util/scanner_hasnextint_radix.html
https://blog.csdn.net/qq_41718454/article/details/104733740
在递归的时候进行结果输出这个问题看似简单,但是执行起来不容易(for me),别笑
题目:
分析:
为了测试算法的正确性,我们带入题目给出的求正整数6划分个数来验证。一开始划分数为0。正整数6可以由一些加数通过加法相加来得到,为了方便分析,先把这些加数的集合定义为A。
第一步:带入q(6,6),进入函数之后,符合n = = m这中情况,那么此时首先是增加了第一种情况,就是集合A中最大的加数为6的时候,只有一种情况,即6 = {6},在划分数加1之后,继续递归计算集合A中最大加数为5的所有划分个数,也就是q(6,5)。所以当n = = m时,公式:q(n,m) = 1 + q(n, n-1)是正确的。
第二步:这一步计算的是q(6,5),符合第四种情况(n>m>1)。当集合A中最大加数已经确定是5之后,那么只需要计算6-5=1不大于5的所有划分数就是整数6的所有以5开头的加法因子的划分数,也就是q(1,5)。在计算最大加数为5的划分数之后,还需要继续递归计算最大加数为4的划分数,也就是q(6,4)。所以当n>m>1时,公式q(n,m) = q(n,m-1) + q(n-m,m)是正确的。
第三步:因为上一步有两个递归公式,首先先考虑q(n-m,m)也就是q(1,5),当最大加数为1的时候,由情况一可以知道只有加数是1这一种情况,在这里就可以确定集合A中一最大加数为5开头的所有划分情况只有一种,就是6={5+1}。这里有根据公式q(n,m) = q(n,m-1) + q(n-m,m)接着把q(6,4)划分为q(6,3)+q(6-4,2)。
后面的情况几乎和前面3步分析的情况差不多。所以我们可以根据上面的递归公式来计算出任意一个正整数的所有划分数。但是题目还需要打印所有划分的情况,那么我们可以定义一个全局数组变量int a[],来记录每一个加数,所以我们可以把上述所说的递归方程q(n,m)改成q(n,m,i),这里的i表示数组a的下标,那么在每一次递归方法中,第一个操作就是赋值给数组,也就是执行操作a[i]=m。
代码上菜
package com.htu.test;
import java.util.Scanner;
public class ZhengShuHuaFen {
static int[] a = new int[1000];//静态数组a用来装每一个小列表
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner in = new Scanner(System.in);
while(in.hasNextInt()) {//检测输入内容是否为数字
int n = in.nextInt();
int count = q(n, n, 0);//count就是题中的p(n),是总的结果种类数,i从0开始,由于是递归,传一次参数就够了
System.out.println(count);
}
}
//递归函数q(n,m,i),这里比书上多了i,目的是为了打印具体划分情况,而且每次都是在return调用之前打印当前情况
public static int q(int n, int m, int i) {
if(n < m) {//m给的过头了就要收缩
return q(n, n, i);
}
a[i] = m;//在经过了上面的的if检测之后,接下来的m就是“合格的”m了,即m<=n,把m存起来,当然m是会变的,不过i也在变
//我们要达到的效果就是每个小列表[m,......]
if(n == 0 || m == 0) {
//打印下标从0到i
printPartition(i);
return 0;
}
if(n == 1 || m == 1) {
if(n == 1) {
//打印下标从0到i
printPartition(i);
}
else q(n-1, 1, i+1);
return 1;
}
if(n == m) {
//打印下标从0到i
printPartition(i);
return 1 + q(n, n-1, i);
}
return q(n-m, m, i+1) + q(n, m-1, i);//i+1代表小列表里面的下一位
}
//展示分类的具体结果,i其实就是每个小列表[]里面的分子数,比如[5+1]里面的5或1
public static void printPartition(int i) {
System.out.print("[");
//只要有结果列表,内部必是循环
//功能:对a数组从0打印到i
for(int j = 0; j <= i; j++) {
if(j == i) System.out.print(a[j]);//打印小列表里面的最后一个小数字
else System.out.print(a[j] + "+");
}
System.out.println("]");
}
}
汉诺塔
对于题目我就不引用了,毕竟个人笔记嘛,若是不小心看到这个文章,见谅哈
package cn.htu.test;
public class Hanoi {
public static void main(String[] args) {
int nDisks = 3;//总的碟片数
doTowers(nDisks, 'A', 'B', 'C');
}
//topN的寓意其实就是每次要挪的碟片,至于挪动的过程建议看一下小甲鱼的动画,理解更深刻
//from是挪动源
//to是挪动目标位置
//inter是中间,就是通过谁去挪
public static void doTowers(int topN, char from, char inter, char to) {
if (topN == 1){
System.out.println("Disk 1 from "
+ from + " to " + to);
}else {
doTowers(topN - 1, from, to, inter);
System.out.println("Disk "
+ topN + " from " + from + " to " + to);
doTowers(topN - 1, inter, from, to);
}
}
}