ALGO_22数的分法

题目

在这里插入图片描述

测试用例

在这里插入图片描述

解析

本题在笔者烦躁的情况下逼着自己去做的,第一次做的时候效果不是很好,想的是动态规划的方法,最终未作出,去睡觉了。早上想着继续做,感觉回溯可行。尝试一下,果真如此,继而又把动态规划的方法写了出来。

回溯

如果告诉你一个数n,分成3份,有多少种方法,你会怎么做?
本着先把题目做出来的想法,笔者会设置i,j,k三个循环变量,j>=i,k>=j。以此保证不会出现1 1 5 与 5 1 1 或1 5 1 重复的情况出现。
想到这里,我们可以看到与回溯的思路很像,做出选择–>下一层–>撤销选择,来代替循环。
选择列表是对于i来说是[1~n/3],对于j来说是[1~n/2],对于k来说是[1~n],那么我们可以设置三个传递的参数。来记录不同的选择范围。

    public static void backtrack(int i,int n,int k){
        if (n==1 && k>1){
            return;
        }
        if (k==1){
            count++;
            return;
        }
        for (int j=i;j<=n/k;j++){
         backtrack(j,n-j,k-1);
        }
    }

完整代码

import java.util.Scanner;

public class ALGO_22 {
    public static int count = 0;
    //数的划分
    //将整数n分成k份,且每份不能为空,任意两份不能相同(不考虑顺序)。
    //  例如:n=7,k=3,下面三种分法被认为是相同的。
    //  1,1,5; 1,5,1; 5,1,1;
    //  问有多少种不同的分法。
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int k = scanner.nextInt();
        backtrack(1,n,k);
        System.out.println(count);
    }
    //使用的回溯
    public static void backtrack(int i,int n,int k){
        if (n==1 && k>1){
            return;
        }
        if (k==1){
            count++;
            return;
        }
        for (int j=i;j<=n/k;j++){
         backtrack(j,n-j,k-1);
        }
    }
}

动态规划

对于将7划分为3份,我们可以把问题等价于如下:
将1划分为2份,余下6 的方法加上
将2划分为2份,余下5 的方法加上
将3划分为2份,余下4 的方法的方法加上
将4划分为2份,余下3
为什么最后一个不行呢?因为1 1 5 和5 1 1 看作是同一个,所以我们的三个数字选用递增的方式。那么就不能到4。而这个4是怎么求出来的呢?想一下,7如果均匀分配的话,那么平均每个为7/3。我们的第一个数是不可能大于7/3的。那么就可以把界限设定在7/3。给的是n和k,那么界限就是n/k。

  • base case是当数分为1份时,只能有1种方法。

完整代码

import java.util.Scanner;

public class ALGO_22b {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int k = scanner.nextInt();
        int[][] dp = new int[n+1][k+1];
        //dp[i][j] :整数i,分成j份,有dp[i][j]种方法
        //base case 整数i分为1份,只有一种分法
        for (int i=1;i<=n;i++){
            dp[i][1] = 1;
        }
        //dp转移
        for (int i=2;i<=n;i++){
            for (int j=2;j<=k;j++){
                for (int m=1;m<=n/k;m++){
                    dp[i][j] += dp[m][j-1];
                }
            }
        }
        System.out.println(dp[n][k]);
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值