uva 10910 Marks Distribution

数学题(组合数学 或者 DP递推都可以)

题意就不说了比较好懂。

这题如果按照题意去模拟着思考是很难解决的,我们换一种思维,抓住一个特殊条件,将问题进行转化。无论是组合数学的方法计算还是DP递推,都是转化问题后才进行的。我们看做有n个不同的盒子,很t个完全相同重量为1的球。按照题意,每个盒子的重量不能小于p,那么我们干脆先每个盒子都放p个小球。剩下m=t-n*p个小球。那么无论这m个小球怎么放都是符合要求的。所以问题就转化为,将m个完全相同的小球放在n个不同的盒子里,盒子内可以装一个或多个小球或者为空。

组合数学:

即便是“将m个完全相同的小球放在n个不同的盒子里,盒子内可以装一个或多个小球或者为空”,这样的问题都不好办。我们再将问题转化,可以看作一共有m+n-1个相同的盒子,从中抽出m个,那么将剩下n-1个盒子,n-1个盒子会产生n个空隙,其实这个就是原问题,可以看做抽出来的这m个盒子就来自于这n个空隙。  所以我们要求的就是 C(m,m+n-1)

#include <cstdio>
int n,t,p,m;

long long C(int a ,int b)
{
    if(b-a<a) a=b-a;
    long long ans=1;
    for(int i=1; i<=a; i++)
        ans=ans*(b-i+1)/i;
    return ans;
}
int main()
{
    int Case;
    scanf("%d",&Case);
    while(Case--)
    {
        scanf("%d%d%d",&n,&t,&p);
        m=t-n*p;
        n=n-1+m;
        printf("%lld\n",C(m,n));
    }
    return 0;
}

DP递推:“将m个完全相同的小球放在n个不同的盒子里,盒子内可以装一个或多个小球或者为空”

一开始想以球数为准进行递推,不难发现这样会重复,所以转化为以盒子为准递推

比如在前4个盒子里放10个球。若前3个盒子放了10个球,第4个盒子为空;若前3个盒子放了9个球,第4个盒子放1个球;若前3个盒子放了8个盒子,第4个盒子放2个球………………若前3个盒子放了0个球,那么第4个盒子放10个球

dp[i][j]表示在前i个盒子里放j个小球的方案数

方程为 dp[i][j]=dp[i-1][0]+dp[i-1][1]+dp[i-1][2]+dp[i-1][3]……+dp[i-1][j]

初始化dp[i][0]=1  ,不放盒子时只有唯一的可能

#include <cstdio>
#include <cstring>
#define N 75
int dp[N][N];

int main()
{
    int Case,n,t,p,m;
    scanf("%d",&Case);
    while(Case--)
    {
        scanf("%d%d%d",&n,&t,&p);
        m=t-n*p;
        memset(dp,0,sizeof(dp));
        for(int i=0; i<=n; i++) dp[i][0]=1;

        for(int i=1; i<=n; i++)
            for(int j=1; j<=m; j++)
                for(int k=0; k<=j; k++)
                    dp[i][j]+=dp[i-1][k];
        printf("%d\n",dp[n][m]);
    }
    return 0;
}

最后总结一下求C(a,b)的模板

long long C(int a ,int b)
{
    if(b-a<a) a=b-a;
    long long ans=1;
    for(int i=1; i<=a; i++)
        ans=ans*(b-i+1)/i;
    return ans;
}
原文地址:https://www.cnblogs.com/scau20110726/p/2889947.html