【编程马拉松】【024-放苹果】

【编程马拉松算法目录】


【024-放苹果】【工程下载>>>】


1 题目描述



  把 M 个同样的苹果放在 N 个同样的盘子里,允许有的盘子空着不放,问共有多少种不同的分法?

  注意:5、1、1 和 1、5、1 是同一种分法,即顺序无关。

1.1 输入描述:



  输入包含多组数据。

  每组数据包含两个正整数 m和n(1≤m, n≤20)。

1.2 输出描述:



  对应每组数据,输出一个整数k,表示有k种不同的分法。

1.3 输入例子:


7 3

1.4 输出例子:


8

2 解题思路


2.1 解法一



  放苹果,后一个盘子不能比前一个盘子放的平果数多。可以用动态规划算法实现,但是存在子问题重叠,时间复杂度高。

2.2 解法二



  设f(m,n)为m个苹果,n个盘子的放法数目,则先对n作讨论,

   * 当n>m:必定有n-m个盘子永远空着,去掉它们对摆放苹果方法数目不产生影响。即if(n>m)f(m,n)=f(m,m)

   * 当n<=m:不同的放法可以分成两类:

    (a)有至少一个盘子空着,即相当于f(m,n)=f(m,n-1);

    (b)所有盘子都有苹果,相当于可以从每个盘子中拿掉一个苹果,不影响不同放法的数目,即f(m,n)=f(m-n,n).而总的放苹果的放法数目等于两者的和,即f(m,n)=f(m,n-1)+f(m-n,n)递归出口条件说明:当n=1时,所有苹果都必须放在一个盘子里,所以返回1;当没有苹果可放时,定义为1种放法;递归的两条路,第一条n会逐渐减少,终会到达出口n==1;第二条m会逐渐减少,因为n>m时,我们会returnf(m,m) 所以终会到达出口m==0.

  综上递推公式为:

f(m,n)=? ? ? 1f(m,m)f(m,n?1)+f(m?n,n) m=0orn=1n>m>0m≥n>1

2.3 解法三



  该问题可以变形为:求将一个整数m划分成n个数有多少种情况,其公式为:

dp[m][n]={1dp[m?n][n]+dp[m?1][n?1] n=1n>1

  对于变形后的问题,存在两种情况:

  (a) n份中不包含1的分法,为保证每份都>=2,可以先拿出n个1分到每一份,然后再把剩下的m-n分成n份即可,分法有:dp[m-n][n]

  (b) n份中至少有一份为1的分法,可以先那出一个1作为单独的1份,剩下的m-1再分成n-1份即可,分法有:dp[m-1][n-1]

  要求可以放苹果的数,m可以被划分为1到k(k=min{n,m}),所以总的方置方法数有dp[m][1]+…+dp[m][k]

  这种方式和解法二非常相似,只是思考的角度不一样。

3 算法实现


import java.util.Scanner;

/**
 * Author: 王俊超
 * Time: 2016-05-24 20:28
 * CSDN: http://blog.csdn.net/derrantcm
 * Github: https://github.com/Wang-Jun-Chao
 * Declaration: All Rights Reserved !!!
 */
public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
//        Scanner scanner = new Scanner(Main.class.getClassLoader().getResourceAsStream("data.txt"));
        while (scanner.hasNext()) {
            int m = scanner.nextInt();
            int n = scanner.nextInt();

            System.out.println(placeApple4(m, n));
        }

        scanner.close();
    }

    /////////////////////////////////////////////////////////////////////////////////////
    // 【解法三】
    /////////////////////////////////////////////////////////////////////////////////////

    /**
     * 放苹果
     * 变形:求将一个整数m划分成n个数有多少种情况
     * dp[m][n] = dp[m-n][n] + dp[m-1][n-1]; 对于变形后的问题,存在两种情况:
     * 1. n 份中不包含 1 的分法,为保证每份都 >= 2,可以先拿出 n 个 1 分到每一份,
     *      然后再把剩下的 m- n 分成 n 份即可,分法有: dp[m-n][n]
     * 2. n 份中至少有一份为 1 的分法,可以先那出一个 1 作为单独的1份,剩下的 m- 1 再分成 n- 1 份即可,
     *      分法有:dp[m-1][n-1]
     * 3. 要求可以放苹果的数,m可以被划分为1到k(k=min{n, m}),所以总的方置方法数有dp[m][1]+...+dp[m][k]
     * @param m 苹果个数
     * @param n 盘子个数
     * @return 共的放法数目
     */

    /**
     * 【非递归实现】
     * 放苹果
     *
     * @param m 苹果个数
     * @param n 盘子个数
     * @return 共的放法数目
     */
    private static int placeApple4(int m, int n) {
        int row = m + 1;
        int col = n + 1;
        // 最多可以放的盘子个数
        int min = Math.min(m, n);

        int[][] dp = new int[row][col];

        // 只有一个盘子时,则只有一种放法
        for (int i = 1; i < row; i++) {
            dp[i][1] = 1;
        }

        for (int i = 1; i < row; i++) {
            for (int j = 2; j < col; j++) {
                if (i > j) {
                    dp[i][j] = dp[i - j][j] + dp[i - 1][j - 1];
                } else if (i == j) {
                    dp[i][j] = 1;
                }
            }
        }

        int rst = 0;
        for (int i = 1; i <= min; i++) {
            rst += dp[m][i];
        }

        return rst;
    }

    /////////////////////////////////////////////////////////////////////////////////////
    // 【解法二】
    /////////////////////////////////////////////////////////////////////////////////////

    /**
     * 解题分析:
     * 设f(m,n) 为m个苹果,n个盘子的放法数目,则先对n作讨论,
     * 当n>m:必定有n-m个盘子永远空着,去掉它们对摆放苹果方法数目不产生影响。即if(n>m) f(m,n) = f(m,m)
     * 当n<=m:不同的放法可以分成两类:
     * 1、有至少一个盘子空着,即相当于f(m,n) = f(m,n-1);
     * 2、所有盘子都有苹果,相当于可以从每个盘子中拿掉一个苹果,不影响不同放法的数目,即f(m,n) = f(m-n,n).
     * 而总的放苹果的放法数目等于两者的和,即 f(m,n) =f(m,n-1)+f(m-n,n)
     * 递归出口条件说明:
     * 当n=1时,所有苹果都必须放在一个盘子里,所以返回1;
     * 当没有苹果可放时,定义为1种放法;
     * 递归的两条路,第一条n会逐渐减少,终会到达出口n==1;
     * 第二条m会逐渐减少,因为n>m时,我们会return f(m,m) 所以终会到达出口m==0.
     */

    /**
     * 【非递归实现】
     * 放苹果
     *
     * @param m 苹果个数
     * @param n 盘子个数
     * @return 共的放法数目
     */
    private static int placeApple3(int m, int n) {

        int row = m + 1;
        int col = n + 1;

        int[][] dp = new int[row][col];

        for (int i = 0; i < row; i++) {
            dp[i] = new int[n + 1];
        }

        for (int i = 0; i < row; i++) {
            for (int j = 1; j < col; j++) {

                if (i == 0 || j == 1) {
                    dp[i][j] = 1;
                    continue;
                }

                if (j > i) {
                    dp[i][j] = dp[i][i];
                } else {
                    dp[i][j] = dp[i][j - 1] + dp[i - j][j];
                }
            }
        }

        return dp[m][n];
    }

    /**
     * 【递归实现】
     * 放苹果
     *
     * @param m 苹果个数
     * @param n 盘子个数
     * @return 共的放法数目
     */
    private static int placeApple2(int m, int n) {  //m个苹果放在n个盘子中共有几种方法
        //因为我们总是让m>=n来求解的,所以m-n>=0,所以让m=0时候结束,如果改为m=1,
        //则可能出现m-n=0的情况从而不能得到正确解
        if (m == 0 || n == 1) {
            return 1;
        }

        if (n > m) {
            return placeApple2(m, m);
        } else {
            return placeApple2(m, n - 1) + placeApple2(m - n, n);
        }
    }

    /////////////////////////////////////////////////////////////////////////////////////
    // 【解法一】下面的方法时间复杂度过高,发生了子问题重叠
    /////////////////////////////////////////////////////////////////////////////////////

    /**
     * 放苹果
     *
     * @param m 苹果个数
     * @param n 盘子个数
     * @return 共的放法数目
     */
    private static int placeApple(int m, int n) {

        // 用于保存结果
        int[] rst = {0};
        // 第一个盘子数放的苹果数
        placeApple(m, n, m, rst);
        // 下面和上面一行实现同样的效果
//        for (int i = m; i >= 0; i--) {
//            placeApple(i, n - 1, m - i, rst);
//        }

        return rst[0];
    }

    /**
     * 放苹果,后一个盘子不能比前一个盘子放的平果数多
     *
     * @param max  当前盘子最多可以放多少个苹果
     * @param n    剩下要放的盘子数目
     * @param left 剩下的苹果数目
     * @param rst  保存结果
     */
    private static void placeApple(int max, int n, int left, int[] rst) {

        // 放最后可以放的盘子
        if (n == 1) {
            // 还剩下left个,不能为负数,可以选择的数目大于剩下的数目
            if (max >= left && left >= 0) {
                rst[0]++;
            }
        }
        // 不是最后一个可以
        else if (n > 1) {
            // 当前盘子可以放[0,max个]
            for (int i = max; i >= 0; i--) {
                placeApple(i, n - 1, left - i, rst);
            }

        }
    }
}

4 测试结果



5 其它信息



因为markddow不好编辑,因此将文档的图片上传以供阅读。Pdf和Word文档可以在Github上进行【下载>>>】

时间: 2024-10-07 09:56:14

【编程马拉松】【024-放苹果】的相关文章

【编程马拉松】【021-数据库连接池】

[编程马拉松算法目录] [021-数据库连接池][工程下载>>>] 1 题目描述 Web系统通常会频繁地访问数据库,如果每次访问都创建新连接,性能会很差.为了提高性能,架构师决定复用已经创建的连接.当收到请求,并且连接池中没有剩余可用的连接时,系统会创建一个新连接,当请求处理完成时该连接会被放入连接池中,供后续请求使用. 现在提供你处理请求的日志,请你分析一下连接池最多需要创建多少个连接. 1.1 输入描述: 输入包含多组数据,每组数据第一行包含一个正整数n(1≤n≤1000),表示请求

hdu 4542 数论 + 约数个数相关 腾讯编程马拉松复赛

题目:http://acm.hdu.edu.cn/showproblem.php?pid=4542 小明系列故事--未知剩余系 Time Limit: 500/200 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others) Total Submission(s): 889    Accepted Submission(s): 207 Problem Description "今有物不知其数,三三数之有二,五五数之有三,七七数之有

【递归】放苹果

问题 : [递归]放苹果 题目描述 楚继光刚把油拿到厨房,老妈又大声喊道:“快去把苹果洗了放到盘子里去.” 楚继光要把M个同样的苹果放在N个同样的盘子里,允许有的盘子空着不放,问共有多少种不同的分法?(用K表示)5.1.1和1.5.1 是同一种分法. 输入 第1行为一个整数,表示测试数据的数目(测试数据的数目t(0 ≤ t ≤20),第2行为M和N(M和N,以空格分开.1≤M,N≤10). 输出 输出有多少种不同分法. 样例输入 1 7 3 样例输出 8 #include <iostream>

oj放苹果

题目描述 把M个同样的苹果放在N个同样的盘子里,允许有的盘子空着不放,问共有多少种不同的分法?(用K表示)5,1,1和1,5,1 是同一种分法. 输入 每个用例包含二个整数M和N.0<=m<=10,1<=n<=10.<=n<=10<=m<=10 样例输入 7 3 样例输出 8 分析: 假设对于m个苹果,n个盘子共有apple(m,n)种方法,那么要想办法递归将m,n的值减小,首先设置递归条件,当m<=1或者n<=1时,apple(m,n)=1:

HDU 4508 湫湫系列故事——减肥记I (2013腾讯编程马拉松初赛第一场)

http://acm.hdu.edu.cn/showproblem.php?pid=4508 题目大意: 给定一些数据. 每组数据以一个整数n开始,表示每天的食物清单有n种食物. 接下来n行,每行两个整数a和b,其中a表示这种食物可以带给湫湫的幸福值(数值越大,越幸福),b表示湫湫吃这种食物会吸收的卡路里量. 最后是一个整数m,表示湫湫一天吸收的卡路里不能超过m. 思路: 完全背包. 一开始以为是01背包. 敲了01后样例2不对啊!!! 然后改成完全就过了..就改循环体就好了.. #includ

放苹果(暴力)

放苹果 Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 33214   Accepted: 20600 Description 把M个同样的苹果放在N个同样的盘子里,允许有的盘子空着不放,问共有多少种不同的分法?(用K表示)5,1,1和1,5,1 是同一种分法. Input 第一行是测试数据的数目t(0 <= t <= 20).以下每行均包含二个整数M和N,以空格分开.1<=M,N<=10. Output 对

【编程马拉松】【027-最短编辑距离】

[编程马拉松算法目录] [027-最短编辑距离][工程下载>>>] 1 题目描述 1.1 输入描述: UNIX系统下有一个行编辑器ed,它每次只对一行文本做删除一个字符.插入一个字符或替换一个字符三种操作.例如某一行的内容是"ABC",经过把第二个字符替换成"D".删除第一个字符.末尾插入一个字符"B",这三步操作后,内容就变成了"DCB".即"ABC"变成"DCB"需

放苹果

描述 题目描述 把M个同样的苹果放在N个同样的盘子里,允许有的盘子空着不放,问共有多少种不同的分法?(用K表示)5,1,1和1,5,1 是同一种分法. 输入 每个用例包含二个整数M和N.0<=m<=10,1<=n<=10.<=n<=10<=m<=10 样例输入 7 3 样例输出 8 /** * 计算放苹果方法数目 * 输入值非法时返回-1 * 1 <= m,n <= 10<><= m,n <= 10<> * @

poj 1664 放苹果 递归

题目链接: http://poj.org/problem?id=1664 题目描述: 有n个苹果,m个盒子,盒子和苹果都没有顺序,盒子可以为空,问:有多少种放置方式? 解题思路: 当前有n个苹果,m个盒子. (1):假设当前最少的盒子放置一个苹果,则给m个盒子分别放一个苹果,剩下n-m个苹果. (2):假设当前最少的盒子不放苹果,则剩m-1个box,n个苹果. 代码: 1 #include <cstdio> 2 #include <cstring> 3 #include <c