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份,6(9-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份,8(9-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 }
原文地址:https://www.cnblogs.com/loganlzj/p/10134287.html