将一个正整数N分解成几个正整数相加,可以有多种分解方法,例如7=6+1,7=5+2,7=5+1+1,…。编程求出正整数N的所有整数分解式子。
输入格式:
每个输入包含一个测试用例,即正整数N (0<N≤30)。
输出格式:
按递增顺序输出N的所有整数分解式子。递增顺序是指:对于两个分解序列N1={n1,n2,⋯}和N2={m1,m2,⋯},若存在i使得n1=m1,⋯,ni=mi,但是ni+1<mi+1,则N1序列必定在N2
序列之前输出。每个式子由小到大相加,式子间用分号隔开,且每输出4个式子后换行。
输入样例:
7
输出样例:
7=1+1+1+1+1+1+1;7=1+1+1+1+1+2;7=1+1+1+1+3;7=1+1+1+2+2
7=1+1+1+4;7=1+1+2+3;7=1+1+5;7=1+2+2+2
7=1+2+4;7=1+3+3;7=1+6;7=2+2+3
7=2+5;7=3+4;7=7
思路解析:
本题是深度优先搜索遍历的典型应用。在清华大学严蔚敏老师《数据结构》一书,第6章 图的遍历 一节中有涉及到此类遍历算法的讲解。具体内容请各位同学自行翻阅。限于篇幅,笔者仅就本题做出讨论。
深度优先搜索是一种基于递归思想而产生的一种遍历方法。如对 n = 5进行分解,我们先设计以下方案:
1 1 1 1 1 成立
1 1 1 2 成立
1 1 2 2 不成立
1 1 3 成立
1 2 不成立
1 2 2 成立
1 3 3 不成立
1 4 成立
2 2 2 不成立
2 3 成立
3 3 不成立
4 4 不成立
5 成立
观察可发现,当和大于等于n时,抛弃当前指针位,指针回到前一位并将数值进行自增,每增一次进行一次和校验;当校验结果小于n时,保留原数组,指针再向右移一位同时把前一位的值赋给当前指针位,紧接着再进行校验。直到数组中只剩下一个数且值为n时结束。
设pos为指针,array为试探数组,储存每组的试探数据,x为当前指针所指的元素。将以上思想用流程图描述如下:
基于此我们可将判断+处理功能封装为一个函数,利用递归实现循环,示例代码如下:
import java.util.Scanner;
public class Main {
/*
* 深度优先搜索遍历的应用
*/
//定义全局共享变量
static int sum = 0;//存放和
static int pos = -1;//指针
static int n = 0;//初始化n为0
static int count = 0;//得到解的次数
static int[] reslut;//试探数组
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
reslut = new int[n];
DFS(1);
}
private static void DFS(int x) {
if (sum == n) {// 得到一组解
count++;
System.out.print(n + "=");
for (int i = 0; i < pos; i++) {// 输出前pos-1个元素
System.out.print(reslut[i] + "+");
}
if (count % 4 == 0 || reslut[pos] == n) {// 此时换行,注意换行时不再打印分号。
//而且最后一位即 n = n时也不需要打印费分号。
System.out.println(reslut[pos]);
}else {
System.out.print(reslut[pos]+";");//单独处理第pos个元素
}
return;// 返回上一层
}
if (sum > n) {// 此时是超解的
return;// 啥都不干,直接退回上一层
}
for (int i = x; i < n + 1; i++) {//把i的初值赋为x,目的就是保持序列自增,防止出现(x+1)+x情况
reslut[++pos] = i;// 要先自增再赋值,也会减少中间变量,节省空间
sum += i;
DFS(i);// 进入递归,其中一个就是检查作用
pos--;// 回到初始位置
sum -= i;// 清空该位
}
}
}