天天看点

XDOJ1144

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;
}