求和 牛客网 动态规划详解

牛客网题目链接: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便找到了所有的组合。
希望可以对你有所帮助。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值