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 }
  • 相关阅读:
    ASP.NET在禁用视图状态的情况下仍然使用ViewState对象【转】
    Atcoder Regular Contest 061 D Card Game for Three(组合数学)
    Solution 「CERC 2016」「洛谷 P3684」机棚障碍
    Solution 「CF 599E」Sandy and Nuts
    Solution 「洛谷 P6021」洪水
    Solution 「ARC 058C」「AT 1975」Iroha and Haiku
    Solution 「POI 2011」「洛谷 P3527」METMeteors
    Solution 「CF 1023F」Mobile Phone Network
    Solution 「SP 6779」GSS7
    Solution 「LOCAL」大括号树
  • 原文地址:https://www.cnblogs.com/loganlzj/p/10134287.html
Copyright © 2011-2022 走看看