poj 1942 组合数问题

来源:互联网 发布:淘宝收复女短裤 编辑:程序博客网 时间:2024/06/05 21:10

本题很简单,但处理方法也很特殊,用double来存中间结果,神!

同样的公式,不同的处理方法,效果不同。 如:选择n时一定选小的,节约时间。

 

处理阶乘有三种办法:

(1)       传统意义上的直接递归,n的规模最多到20+,太小了,在本题不适用,而且非常慢

(2)       稍快一点的算法,就是利用log()化乘为加,n的规模虽然扩展到1000+,但是由于要用三重循环,一旦n规模变得更大,耗时就会非常之严重,时间复杂度达到O(n*m*(n-m)),本题规定了n,m用unsigned int32类型,就是说n,m的规模达到了21E以上,铁定TLE的。而且就算抛开时间不算,还存在一个致命的问题,就是精度损失随着n的增加会变得非常严重。

因为n有多大,就要进行n次对数运算,n规模一旦过大,就会丢失得非常严重了。所以这种方法是绝对不可取的,因为中途的精度丢失不是简单的四舍五入可以挽回的。

(3)       拆分阶乘,逐项相除,再乘以前面所有项之积。这种方法用一个循环就OK了,时间复杂度只有O(n-m),非常可观。

 

 

#include<iostream>#include<cstdio>#include<string>#include<cstring>#include<algorithm>#include<cmath>using namespace std;int main(){    long long n,m,i;    double a,ans;    while(scanf("%lld%lld",&n,&m))    {        if(m+n==0)            break;        if(n>m)            swap(n,m);  //注意n取小的        a=m+n;        ans=1;        for(i=1;i<=n;i++)        {            ans*=((a-i+1.0)/(i+0.0));  //边约边乘        }        printf("%.0f\n",ans);    }    return 0;}