Problem 1144 - 组合数学三之棋魂
Time Limit: 1000MS
Memory Limit: 65536KB
Difficulty:
Total Submit: 61
Accepted: 24
Special Judge: No
Description
嗯,首先是故事预告:小学六年级的进藤光为了赚些零用钱,跑到爷爷家里寻宝,偶然翻出了一个旧棋盘。接触棋盘的一瞬间,附身于棋盘中的平安时代棋士——藤原佐为的灵魂进入了小光的体内。佐为将围棋视为生命,在他的熏陶下,小光也逐渐对围棋产生了兴趣……
个人认为围棋中最为枯燥乏味的莫过于整理棋子,比如现在光所要做的工作。先有n个黑色的棋子,m个一样的盒子,问:光有多少种不同的方法,可以将棋子全部放到盒子中去??
Input
每一行有一个m和n(1<m<=n<1000)
Output
每一行输出一个可能的个数(模10007取余)
Sample Input
2 4
1 5
Sample Output
3
1
这题看到后我就想到了转化成拆分数来处理。
他的模型原型就是n个相同小球放进m个相同盒子且不允许出现空盒的方法。。上篇文章中只说是dp处理,没说怎么处理,那么这篇文章就谈谈这种方法。都说了是拆分数了,那么直接看看拆分数吧。
所谓拆分数就是把给定的一个整数拆成几个正整数之和的方法数。(且其中最大加数m不大于n)
如给定整数为n=6;
则可以拆分为 :6, 5+1,4+2, 4+1+1, 3+3, 3+2+1, 3+1+1+1,2+2+2, 2+2+1+1, 2+1+1+1+1, 1+1+1+1+1+1;
总共11种方法。
下面分析一下这个一般性的规律:
假设用dp[n][m]来表示把n拆分(且其中m为最大加数)的方法数;
那么,首先第一种情况:
(1): 当n或m等于1时只有1种拆分方式,从上面的例子很明显可以看出。
(2): 下面看n和m的关系来讨论.
当m>n时,实际上最大加数m不可能大于n,故这种情况下dp[n][m]=dp[n][n];
当m==n时,从上面的例子可以观察出来,此时的情况数为不大于n的最大加数拆分一下再加上1就行,即dp[n][m]=dp[n][m-1]+1;
当m<n时,也是最普遍的情况。此时恰好就是把n拆分出来且保证了最大加数不大于n的情况。。
观察上述例子,假设m=4的话。此时就是求dp[6][4],这个就相当于最大加数小于4拆分一下的情况再加上6-4=2拆分一下的情况。
此时的方程就是: dp[n][m]=dp[n][m-1]+dp[n-m][m]。
有了上述结论;很容易就能写出本题的代码。
不细心,然后各种CE ,WA。。。
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cstring>
using namespace std;
int m,n;
const int maxn=1000+5;
const int mod=10007;
long long dp[maxn][maxn];
int main()
{
for(int i=1;i<=1001;i++)
dp[i][1]=1;
for(int i=1;i<=1001;i++)
{
for(int j=2;j<=1001;j++)
{
if(i==j)
dp[i][j]=(dp[i][j-1]+1)%mod;
else if(i>j)
dp[i][j]=(dp[i][j-1]+dp[i-j][j])%mod;
else if(i<j)
dp[i][j]=dp[i][i];
}
}
while(scanf("%d%d",&m,&n)!=EOF)
{
printf("%lld\n",dp[n][m]);
}
return 0;
}