组合数的代码实现

    前述:又是忙碌的一周,基本前三天都在看资料,大概是星期三晚上之前看完了老师发的所有课件,真的只是了解了知识点 ,对怎么码题一无所知,晚上的英语课去老师的博客里偷来了组合计数的几个博客资料,一直读到十点多,一想还有场练习赛,挑了个a的人最多的题目,花十分钟签了个到就休息了。当然并不是白休息,周四的凌晨0点爬起来看组合数专题的题目,把题干短的题目还有从博客资料里面见过的题目都仔细读了一遍,建立了第一想法,周四一口气a了9个,主要有四类题目,一类简单组合计数,一类卡特兰数模板,一类容斥定理,一类水题。然后做不动了,还需要继续学想法,这9道题只不过是让我入个门,顺便写写博客总结一下9个题让我学会了什么,第一个就是组合数的代码实现。

  组合数有很多的递推公式,但是往往都是不实用的,用C(n,m)=c(n,m-1)+c(n-1,m-1)这种递归方式写程序C(20,10)基本就是极限了,往往没有使用价值。然后能想到的做成代码的公式就只有n!/(m!*(n-m)!)了,以前有个代码模板,乘的过程中直接除,以前恍恍惚惚就认为是对了,今天做练习的时候发现不行了,这个只适用于小范围的组合数的计算,大了就不行了,因为有可能不整除。还是有些胆怯的试了试一个博客中学到的方法,很快ac了(看起来数据量很大的,这样做很怕tle,但是好像是我多虑了)。方法就是分别计算n*(n-1)...*(n-m+1),以及(n-m)!然后每次乘的时候,两式同时除去最大公约数(这也是我担心会超时的地方),结果看起来没有。

 AC代码:(题目链接)

#include
#include
#include
#include
#include
using namespace std;
long long gcd(long long a,long long b){
    return b==0?a:gcd(b,a%b);
}
long long zuhe(long long x,long long y){
    long long i,j,l,r;
    if (x==0||y==0)return 1;
    y=min(y,x-y);
    for (i=1,l=1,r=1;i<=y;i++){
        l=l*(x-i+1);
        r=r*i;
        long long xx=gcd(l,r);
        if (xx<0)xx=-xx;
        l/=xx;
        r/=xx;
    }
    if (r>1)l/=r;
    return l;
}
int main (){
    long long n,m;
    while (cin>>n>>m&&(n||m))
    printf("%lld\n",zuhe(n,m));
}

  这个写法是目前我认为比较科学的一个写法,至少不会出wr,正好趁这个机会码住。

你可能感兴趣的:(数学-组合数)