微软算法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 }
原文地址:https://www.cnblogs.com/aalex/p/4908362.html