牛客网题目链接:https://www.nowcoder.com/questionTerminal/11cc498832db489786f8a03c3b67d02c
题目描述:输入两个整数 n 和 m,从数列1,2,3…n 中随意取几个数,使其和等于 m ,要求将其中所有的可能组合列出来
输入描述:
每个测试输入包含2个整数,n和m
输出描述:
按每个组合的字典序排列输出,每行输出一种组合
示例1
输入
5 5
输出
1 4
2 3
5
博主在拿到这道题的时候首先想到了一个很简单的解决思路:就是用i(i 初始值为1)遍历n,如果m-i <= n的话就可以输出i, m-i的用例。但是这个思路没有考虑到组合的不定项性。只能输出项数为2的情况。但是这个最开始错误的思路其实只要加以改动,就可以帮助我们得到正确的结果。
我们只要在上面思路的基础上,完成可以找出其余项数不为2的情况就可以了。
举例:n = 6 m = 6 开始遍历:6-1=5; 5 <= n。此时将m的值刷新为5,继续去找等于5的情况……很容易要想到为了达到我们的目的需要使用递归来完成。
这里附上在牛客网看到的使用该思路解题的答案链接:https://www.nowcoder.com/questionTerminal/11cc498832db489786f8a03c3b67d02c
先贴代码:
static ArrayList<ArrayList<Integer>> ret = new ArrayList<ArrayList<Integer>>();
static ArrayList<Integer> list = new ArrayList<>();
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while (sc.hasNext()) {
int n = sc.nextInt();
int m = sc.nextInt();
dfs(1,n,m);
for(ArrayList<Integer> l : ret) {
int i = 0;
for (; i < l.size() - 1; i++) {
System.out.print(l.get(i) + " ");
}
System.out.println(l.get(i));
}
}
}
private static void dfs(int index, int n, int count) {
if(count == 0) {
ret.add(new ArrayList<>(list));
}else {
for (int i = index; i <= count && i <= n; i++) {
list.add(i);
dfs(i + 1,n,count - i);
list.remove(list.size() - 1);
}
}
}
我们使用两个static ArrayList来实现我们的目的。
list是用来存储每种符合要求的组合情况。
ret作为一个二维ArrayList(不知道这么叫对不对)来存储所有组合。
static ArrayList<ArrayList<Integer>> ret = new ArrayList<ArrayList<Integer>>();
static ArrayList<Integer> list = new ArrayList<>();
核心代码解读:
private static void dfs(int index, int n, int count) {
if(count == 0) {
ret.add(new ArrayList<>(list));
}else {
for (int i = index; i <= count && i <= n; i++) {
list.add(i);
dfs(i + 1,n,count - i);
list.remove(list.size() - 1);
}
}
}
首先进行解释:
index 代表了当前组合开始的首数字。如 6 6中:1 2 3 。 1就为首数字。因为要求字典序输出。所以第一次进入dfs首数字一定为1。
count :用来表示当前组合m的剩余部分。如 6 6中: 1 2。此时count=3。
以6 6 进行举例:
dfs(1,6,6)进入方法。count = 6进入else。
else中就是遍历n的部分。注意跳出条件为: i <= count && i <= n。
首先将1放入list。list中此时有:1。
dfs递归调用自己dfs(2,6,5)。因为存放结果的组合list中已经有了1了,所以下一个开始找的首数字就变成了2。count也要减掉list中已经有的1。
当list中存放了(1,2,3)时,count = 0,new一个lArrayList加入到存储组合的ret中,index=3的情况下不满足i<=count。
此时递归的index = 3的情况结束。回到index = 2的情况。此时该执行list.remove(list.size() - 1);从list中弹出list的最后一个元素:3。
在index=2的情况下此时应该执行remove操作,将list中的2也拿出来。list中只剩1。
i = 3。将3放入list。list中:1,3。count = 2。不符合进入循环的条件。回到index = 2 的情况,再次将3从list中拿出。
此时 i =4。重复执行i = 3时的情况。
i = 5。重复执行i = 3 的情况。
因为count = 5,所以i= 6的情况不会被循环。
至此在index = 1 的情况下。所有组合都被尝试过了。
最底层的index被刷新为2。
然后在index=2的情况下继续重复index=1 的情况。
重复至index=6便找到了所有的组合。
希望可以对你有所帮助。