Java笔试题:在一个数组中,求出所有和为m的组合

题目描述:

在长度为n的单调递增型数组a[n]中,1 <= a[i] <= 100,编写一个程序打印出和为m的所有组合。

例如:a[] = {1,2,3,5,6},m=11,打印出

5 6

2 3 6

1 2 3 5

对于这道题,我的第一感觉就是道类似0-1背包问题,给出的物品中,选出所有刚好能装满背包的物品

于是开始解决这道题就用回溯法

//rem表示剩余的值,flag用于标记每个位置的数是否被用到,k即为当前要计算的位置
public static void backtrace(int[] a,int m,int rem,boolean[] flag,int k) {
	//剩余值为0了,应该输出并返回
    if (rem == 0) {
		output(a,flag);
		System.out.println();
		return;
	}

	//剩余值小于0,自然要返回了
	if (rem < 0) {
		return;
	}
    
    //位置小于等于0,肯定得返回,不然就栈溢出了
	if (k <= 0) {
		return;
	}
		
	for(int i = k;i >= 0;i--) {
		//标记下当前位置的数被用到
        flag[i] = true;
		rem -= a[i];
        // k-1 往前进行计算
		backtrace(a, m, rem, flag, k-1);
		//这一步回溯,再算一下当前位置的数不被用到的情况
        flag[i] = false;
		rem += a[i];
		backtrace(a, m, rem, flag, k-1);
	}
}

private static void output(int[] a, boolean[] flag) {
	for(int i = 0;i < a.length;i++) {
		if (flag[i]) {
			System.out.print(a[i]+" ");
		}
	}
}

看上去逻辑好像没什么问题,运行一下结果看看

Java笔试题:在一个数组中,求出所有和为m的组合_第1张图片

结果让我大吃一惊,除了第一第二个是对的,后面全部错了

有些结果被重复输出了,说明有些位置的数被重复用了,很明显,这种方法行不通

 

换种方法思考,每个位置的数只有选和不选两种情况,可以用0,1表示

比如 00011 则表示例的数组{1,2,3,5,6}中选了5和6,那么计算结果就是11

所以解决这题的方法可以穷举出所有n(数组长度)位的二进制数,并且把当前位置为1的数相加计算,结果为m即可

public static void binaryCal(int[] a,int m) {
		int n = a.length;
        //最大的数为2的n次方
		int max = 1 << n;
		for(int i = 1;i < max;i++) {
            //转成二进制数
			String binaryNum = Integer.toBinaryString(i);
			//转成相同的位数,不足n位的在前补0
            binaryNum = toSameLen(binaryNum,n);
			char[] bitNum = binaryNum.toCharArray();
			int sum = 0;
			for(int j = 0;j < bitNum.length;j++) {
                //二进制数当前位置为1,则加起来
				if (bitNum[j] == '1') {
					sum += a[j];
				}
			}
            //和为m了,输出
			if (sum == m) {
				output(bitNum,a);
			}
		}
	}
	
	private static String toSameLen(String binaryNum, int len) {
		//数的长度
        int numLen = binaryNum.length();
		if (numLen == len) {
			return binaryNum;
		}
		StringBuilder sb = new StringBuilder();
        //差几位补几个0
		for(int i = 0;i < len - numLen;i++) {
			sb.append(0);
		}
		return sb.append(binaryNum).toString();
	}

	private static void output(char[] bitNum, int[] a) {
		for(int i = 0;i < bitNum.length;i++) {
			if (bitNum[i] == '1') {
				System.out.print(a[i]+" ");
			}
		}
		System.out.println();
	}

以上代码运行结果

Java笔试题:在一个数组中,求出所有和为m的组合_第2张图片

跟范例输出的一模一样

虽然结果对了,代码也容易理解,但并不是最好的方法

①没有用到题目给出的单调递增条件

②每个数字都要做一次二进制转换,并且还要补0,太过耗时,如果数组长度越大,那么效率就越低

 

你可能感兴趣的:(Java笔试题:在一个数组中,求出所有和为m的组合)