【PTA】整数分解为若干项之和(java版)

完整代码在最后。

题目描述

将一个正整数N分解成几个正整数相加,可以有多种分解方法,例如7=6+1,7=5+2,7=5+1+1,…。编程求出正整数N的所有整数分解式子。

输入格式

每个输入包含一个测试用例,即正整数N (0

输出格式

按递增顺序输出N的所有整数分解式子。递增顺序是指:对于两个分解序列N1={n1,n2,⋯}和N2={m1,m2,⋯},若存在i使得n1=m1,⋯,ni=mi,但是ni+1

输入样例

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

测试点

测试点 描述
0 样例等价,多行输出
1 一行内输出
2 最小N
3 最大N

思路分析

先根据上面的输出找规律,可以观察到:

  • 最多7个数相加(且全是1),最少1个数相加(且为7)。(可以拿一个数组储存这一排数)
  • 1+1+5如何变成1+2+2+2的,怎么还变长了?说明式子的长度不是越来越短的,而是1+1+5达到7后,第3个数及之后就不能再加数字了,应该把第2个数加一个(1+[2]),然后从第3个数开始变化(1+2+[2],第3个数不能小于第2个数)。

这就说明这需要用到递归。

先写几个成员变量:

public static int N;
public static int[] numbers = new int[35];
public static int interval = 0;

这里N用来存储N,numbers用来表示一排分解数,interval表示公式的个数。

先写个printItem的函数,用来格式化分解数。

public static String printItem(int[] array, int pos) {
	String result = N + "=";
	for (int i = 0; i <= pos; i++) {
		result += array[i];
		if (i < pos) result += "+";
	}
	if (interval % 4 == 0 || pos == 0) result += "\n";
	else result += ";";
	return result;
}

pos表示分解数的位置(即分解数的个数减1),如pos为6就表示输出array[0]array[6]这7个数。

同时要处理+;放的位置,最后得到一条形如7=1+2+2+2;的字符串。

然后写主函数:

public static void main(String[] args) {
	Scanner in = new Scanner(System.in);
	N = in.nextInt();
	for (int i = 1; i <= N / 2; i++) {
		dfs(0, 0, i);
	}
	dfs(0, 0, N);
}

意思是,第一个数从1到N/2(而非N-1,防止7=4+3这种不必要的判断),都要寻找输出公式。再当第一个数为N的时候,单独输出N=N

然后设计函数dfs,它有三个参数,分别是:

public static void dfs(int pos, int sum, int next)

pos表示当前的处理位置sum表示当前的总和next表示下一个要加的数字(函数开始时还没加)。所以主函数里面写dfs(0, 0, i)

在加next之前,需要先判断一下有没有加的必要,比如到7=1+5+?这一步了,possumnext三个参数分别为2、6、5,但是没有加的必要了。

if (sum + next > N) return;

否则,就把这个数加上,也就是把数组当前位置(pos)赋值为这个数(next)。

else {
    numbers[pos] = next;
    ...
}

加上之后,再判断一下,现在这一排数到N了没有,如果到了,就可以输出了。

else {
	numbers[pos] = next;
	if (sum + next == N) {
		interval++;
		System.out.print(printItem(numbers, pos));
	}
	...
}

如果没到,就需要递归,其中递归的参数中有如下变化:

  • pos因为当前位置有数字了,所以当前位置应该加1;
  • sum因为加上当前数字了,所以当前总和应该加next
  • next比较复杂,应该是多少?

比如现在在7=1+1+1+?这一步,pos从2变3,sum从2变3,next应该是多少?

这个递归肯定不止做一次,应该是个循环,因为在7=1+1+1之后,next可以是1,可以是2,可以是3,可以是4,不能是5。

也就是说在7=1+1+1+?这一步,应该依次计算dfs(3, 3, 1)dfs(3, 3, 2)dfs(3, 3, 3)dfs(3, 3, 4),从而得出7=1+1+1+1+1+1+17=1+1+1+1+1+27=1+1+1+1+37=1+1+1+2+27=1+1+1+4这5个式子,至于得到next后怎么处理得到7,那就是重复上面的步骤,也就是递归。

所以上面省略号部分应该这么写:

else if (sum + next < N) {
	sum += next;
	pos++;
	for (int i = next; i <= N - sum; i++) {
		dfs(pos, sum, i);
	}
}

dfsnext要从当前next开始循环(因为下一个数不能小于上一个数),直到N-sum为止(总大小为N,已经加到sum了,最多再加N-sum这么大)。

递归处理完成。

完整代码

import java.util.Scanner;

public class Main {
    public static int N;
    public static int[] numbers = new int[35];
    public static int interval = 0;

    public static String printItem(int[] array, int pos) {
        String result = N + "=";
        for (int i = 0; i <= pos; i++) {
            result += array[i];
            if (i < pos) result += "+";
        }
        if (interval % 4 == 0 || pos == 0) result += "\n";
        else result += ";";
        return result;
    }

    public static void dfs(int pos, int sum, int next) {
        if (sum + next > N) return;
        else {
            numbers[pos] = next;
            if (sum + next == N) {
                interval++;
                System.out.print(printItem(numbers, pos));
            } else if (sum + next < N) {
                sum += next;
                pos++;
                for (int i = next; i <= N - sum; i++) {
                    dfs(pos, sum, i);
                }
            }
        }
    }

    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        N = in.nextInt();
        for (int i = 1; i <= N / 2; i++) {
            dfs(0, 0, i);
        }
        dfs(0, 0, N);
    }
}

你可能感兴趣的:(题库,java,面向对象编程,函数式编程)