码迷,mamicode.com
首页 > 其他好文 > 详细

AlvinZH双掉坑里了

时间:2018-12-17 22:37:44      阅读:217      评论:0      收藏:0      [点我收藏+]

标签:main   logs   跳过   cstring   部分   created   问题:   htm   重复   

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\n", dp[n][m]);
27     }
28 }

 

AlvinZH双掉坑里了

标签:main   logs   跳过   cstring   部分   created   问题:   htm   重复   

原文地址:https://www.cnblogs.com/loganlzj/p/10134287.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!