zoukankan      html  css  js  c++  java
  • AlvinZH双掉坑里了

    AlvinZH双掉坑里了

    时间限制: 1000 ms 内存限制: 65536 kb
     

    题目描述

    AlvinZH双掉进坑里了!

    幸运的是,这坑竟然是宝藏迷宫的入口。这一次AlvinZH机智地带了很多很多背包——装金币!

    假设现在AlvinZH捡到了n块金币,他一共带了m个背包,每个背包可以装任意多金币,但AlvinZH不允许有空的背包。

    请你帮他计算一下一共有多少种装金币的方法吧!

    注意:所有背包看作相同,即{1,3}和{3,1}是同一种方法。

    输入

    输入包含多组数据。

    每组数据包含两个正整数,为金币数n(1≤n≤10^4),背包数m(1≤m≤10^3,且m≤n)。

    输出

    对于每组数据,输出一行,为使用所有背包装金币的方法数(结果对1000007取模)。

    输入样例

    4 2
    9 3

    输出样例

    2
    7

    样例解释

    4:{1,3}{2,2};

    9:{1,1,7}{1,2,6}{1,3,5}{1,4,4}{2,2,5}{2,3,4}{3,3,3}。

    HINT

    这不是简单的背包问题,请勿套公式。

    AlvinZH:其实和背包没有任何关系~

    思路

    简单DP。简化问题:将n个金币放入m个盒子,无空盒。
    
    直接上dp吧,dp[i][j]:将i个金币放入j个盒子的方法数。此题的关键在于如何找到状态转移方程,很有可能会计算重复的方法。我们把答案分成两部分:
    
    ①放完之后所有盒子金币数量大于1;
    ②放完之后至少有一个盒子金币数量为1。
    这样分可以保证不会有重复计算。状态转移方程: dp[i][j]=dp[i−j][j]+dp[i−1][j−1]。
    
    ① dp[i−j][j] :将(i-j)个金币放到j个盒子,然后这j个盒子每个再放1个金币。表示的是将i个金币分成所有盒子金币数量大于1的方案总数。例如,求9分解成3份,69-3)分成3份可以分为{1,1,4}{1,2,3}{2,2,2},则9可以分为{2,2,5}{2,3,4}{3,3,3},共3种。
    
    ② dp[i−1][j−1]:将(i-1)个金币放到(j-1)个盒子,再来一个盒子放1个。表示的是将i个金币分成至少有一个盒子金币数量为1的方案总数。例如,求9分解成3份,89-1)分成2份可以分为{1,7}{2,6}{3,5}{4,4},则9可以分为{1,1,7}{1,2,6}{1,3,5}{1,4,4},共4种。
    
    难点在于如何避免重复,这里处理得十分巧妙,请细细体会。

    以上转自https://www.cnblogs.com/AlvinZH/p/7840604.html#_label3

    之所以将答案分为1,2两种情况,是由于1,2两种情况可以把原问题分为两个不相交的集合,且每个子集的答案都可以以一种方式从规模更小的问题中生成。

    放完后所有盒子金币数大于1:假设”将i个金币放入j个盒子且所有盒子金币数大于1“的放法有k种,那么我从每个盒子拿去一个金币,一定对应子问题dp[i-j][j]的一种放法,即两者的放法数是相等的。

    每个dp[i-j][j]的一种放法,各加一个金币都可以生成现有问题的一个放法;而现有问题的放法各拿去一个金币又可以生成子问题dp[i-j][j]的一种放法,二者一一对应。

    放完后至少有一个盒子金币数量为1:假设”将i个金币放入j个盒子且至少有一个盒子金币数量为1“的放法有k种,那我拿去这个盒子和这个球,一定对应子问题dp[i-1][j-1]的一种放法,即二者放法数是相等的。

     另外关于初始值,将某个非法子问题的dp值置0来使得其贡献为0

    i>=j才能保证没有空包,只对i>=j问题求解,否则保持初始值0,表示该情况不能生成更规模问题的解(贡献为0)。

     另外i,j>=1的才是问题讨论的范围,因此循环i,j从1开始,且将dp[i][0],dp[0][j]也置0

    但注意dp[0][0]要初始化为1(但感觉上应该是初始化do[1][1]=1,只不过初始化dp[0][0]=1碰巧能通过转移方程求出dp[1][1]=1且这样能保持循环的美观,也可以直接初始化dp[1][1]但此时循环中i=1,j=1的情况就要跳过,否则又会被重新写为0)

    参考代码

     1 //
     2 // Created by AlvinZH on 2017/10/23.
     3 // Copyright (c) AlvinZH. All rights reserved.
     4 //
     5 
     6 #include <cstdio>
     7 #include <cstring>
     8 #define MOD 1000007
     9 
    10 int n, m;
    11 int dp[10005][1005];
    12 
    13 int main()
    14 {
    15     while(~scanf("%d %d", &n, &m))
    16     {
    17         memset(dp, 0, sizeof(dp));
    18         dp[0][0] = 1;
    19         for (int i = 1; i <= n; ++i) {
    20             for (int j = 1; j <= m; ++j) {
    21                 if(i - j >= 0)
    22                     dp[i][j] = (dp[i-j][j] + dp[i-1][j-1]) % MOD;
    23             }
    24         }
    25 
    26         printf("%d
    ", dp[n][m]);
    27     }
    28 }
  • 相关阅读:
    信息系统开发内容
    系统开发项目环境
    操作系统学习基础
    进程管理、内存管理、存储管理初步了解
    操作系统导论
    Hadoop学习2
    Hadoop学习
    LiveScript 字面量
    初识LiveScript
    在使用Cocos2d-JS 开发过程中需要用到的单体设计模式
  • 原文地址:https://www.cnblogs.com/loganlzj/p/10134287.html
Copyright © 2011-2022 走看看