微软算法100题21 数列中所有和为特定值的组合
第21 题
编程求解:
输入两个整数n 和m,从数列1,2,3.......n 中随意取几个数,
使其和等于m ,要求将其中所有的可能组合列出来
思路:典型的01背包问题,按照背包问题的思路,可以将问题:f(n,m),分解为f(n-1,m)和[f(n-1,m-n),n]两个子问题
就是N个数里面和等于M的元素组合,一定包含N-1个数里所有和为M的组合,再加上N-1个数里所有和为M-N的组合,再加上N这个元素
为了记录所有符合要求的元素,可以用一个大小为N的整数数组来标记数列1到N的元素状态,如果元素N符合要求,则marks[n-1]=1,否则marks[n-1]=0
这里需要考虑的是何时标记为1?何时标记为0? 对于子问题f(n-1,m-n)来说,我们需要把元素N标记为1,因为如果f(n-1,m-n)有符合要求的元素的话,元素N也符合要求。对于子问题f(n-1,m)来说,我们需要把元素N标记为0,即元素N不符合要求,因为子问题f(n-1,m)已经将N排除在外了。
这里我们使用递归来解决背包问题,最佳方法仍是动态规划,这个留到以后。
1 package com.rui.microsoft; 2 3 4 /** 5 * 第21 题 6 编程求解: 7 输入两个整数n 和m,从数列1,2,3.......n 中随意取几个数, 8 使其和等于m ,要求将其中所有的可能组合列出来 9 * 10 */ 11 public class Test21_01Bag { 12 13 public static void main(String[] args) { 14 int m = 15; 15 int n = 10; 16 int[] marks = new int[n]; 17 Test21_01Bag.find(n, m, marks); 18 } 19 20 public static void print(int[] marks){ 21 for(int i = 0; i < marks.length; i++){ 22 if(marks[i] == 1){ 23 System.out.print(" " + (i+1)); 24 } 25 } 26 System.out.println(""); 27 } 28 29 public static void find(int n, int m, int[] marks){ 30 //n和m的值不能小于1,否则从该次递归返回 31 if(n<1 || m<1)return; 32 //如果n大于m,则应该重置n为m,因为大于m的元素肯定不符合要求 33 if(n>m)n=m; 34 if(n==m){ 35 //如果n==m,说明当前n元素是符合条件的 36 marks[n-1]=1; 37 //打印该组合 38 print(marks); 39 //打印完毕,将该元素n的状态恢复初始值 40 marks[n-1]=0; 41 } 42 43 //先标记元素n,然后递归解决子问题f(n-1,m-n) 44 //因为子问题f(n-1,m-n)所包含的符合条件的元素组合再加上元素n才能得到最终解,所以元素n标记为1 45 marks[n-1]=1; 46 find(n-1,m-n,marks); 47 48 //先标记元素n,然后递归解决子问题f(n-1,m) 49 //此时元素n已经被排除在子问题f(n-1,m)之外了,所以将其标记为0 50 marks[n-1]=0; 51 find(n-1,m,marks); 52 } 53 }