UVa 10207 (组合数的高精度计算)

来源:互联网 发布:阿里云服务器华东2 编辑:程序博客网 时间:2024/06/04 18:29

problem

In this particular problem, TheUnreal Tournamentis a tournament, which consists of only two teams. Letthese two teams beAbahoniandMohamedan. They play in between them notmore than 2n− 1 games, the winnerbeing the first team to achievenvictories.You can assume that there are no tied games, the result of each game isindependent and for any match there is a constant probabilitypthat teamAbahoni will win and hence there is a constant probabilityq= 1 −pthat team Mohamedan willwin.

P(i,j) is the probability that teamAbahoniwill win the series given that they still needimore victories to achieve this,whereas teamMohamedanstill needjmore victories if they are to win. TheP(i,j)can be computed with a function like the following

Function P(i,j)

if i = 0 then return 1 else if j = 0 then return 0 elsereturn pP(i-1,j) + qP(i,j-1)

You will haveto write a program that gives the probability of winning for anyp,iandjand also gives the numberof recursive calls required if the function above is used to get theprobabilityP(i,j).


Input

The input filecontains several sets of input. The first line of a set contains onefloating-point numberp(0< p <1), and an integerN(0 ≤ N < 1001) wherep isthe winning probability ofAbahoniandN is the number queries to follow.Each of the nextNlines contains twointegersi(0 ≤i ≤ 1000) and j(0 ≤j≤ 1000). Input is terminated by a set,which has zero as the value ofN.This set should not be processed.


Output

For each query you should printtwo lines. The first line contains the value ofP(i,j) with five digitsafter the decimal and the second line contains a round number which is thenumber of recursive calls needed if the function mentioned above was used to determinethe value ofP(i,j). If the value ofP(i,j) is undefined you should print ‘-1’ as its value with similarformatting. A blank line should be printed between the outputs of twoconsecutive sets.


Sample Input

0.5 3

1   1

2   2

3   3

0.5 2 10 3

10 2

0.7 0


Sample Output

0.50000

2

0.50000

10

0.50000

38

0.01929

570

0.00586

130


传送门:点击打开链接


思路

题目大致意思是:Aba和Moh要进行若干场比赛,每场比赛Aba获胜的概率是p,Moh获胜的概率是q,其中p和q为正实数,且p+q=1。

若Aba率先获胜i场比赛,则Aba就能获得最终的胜利,否则若Moh率先获胜j场比赛,则Moh获得最终胜利。

题目给出二元函数P(i,j)计算Aba的胜率:

Function P(i,j)if i=0 then return 1else if j=0 then return 0else return p*P(i-1,j)+q*P(i,j-1);EndFunction
求解P(i,j)以及递归求解P(i,j)时调用到上述函数的次数

数据范围:至多1000组数据,每组数据中  i,j<=1000

直接按定义模拟求解需要O(n^2)的复杂度,观察解的形式,看能否降低复杂度,如下图:



最左列1.0表示P(0,k)的值(此时已经不需要进行游戏,Aba胜利,胜率1.0),最底下一列的0.0表示P(l,0)的值(Aba战败,胜率0.0)。而对最终答案P(i,j)的每一份概率贡献对应了从左列某个节点(0,k)出发向右或向上到达(i,j)的路径(第一步必须向右)。向右走,则路径的概率应乘上系数p;向上走,则乘上系数q。

理解:假如现在比赛进行到上图(i,j)点所示阶段,那么下一步有两种选择:Aba胜利,概率为p,向左走;Aba战败,概率为q,向右走。可以看出,到达最左边一列即为Aba胜利的标志

于是从末状态(在最左边一列上)进行思考,从某个最左列起点(0,k)先向右一步到达(1,k),再到达(i,j)的方法数为C(i-1+j-k,i-1),则从(0,k)出发到达(i,j),概率和为C(i-1+j-k,i-1)* p^i * q^j-k。即为:


于是可以只是用O(N)的时间计算P(i,j)。


再考虑递归求解P(i,j)遍历的节点数,设为F(i,j)。通过归纳,得到:

F(0,j)=F(i,0)=0    (递归终止条件,自然为0)

F(i,j)=F(i-1,j)+F(i,j-1)+2

做等量代换,令G(i,j)=F(i,j)+2

有G(i,j)=G(i-1,j)+G(i,j-1)

观察到这个递推式与组合数类似:


考虑通过构造,将G(i,j)与C(n,r)联系起来。令n=i+j且r=i,则G(i,j)=C*C(i+j,i)。其中C为常数,带入初值可得C=2。

所以G(i,j)=2*C(i+j,i),F(i,j)=2*C(i+j,i)-2


代码示例

(过样例,由于本版本高精度问题?未AC,如有改进方法,还请留言,谢谢!另外如果是极限数据,好像会TLE)

//#define LOCAL#include<iostream>#include<algorithm>#include<cstdio>#include<stdlib.h>#include<string>#include<string.h>#include<math.h>using namespace std;//辅助比较函数int compare(string str1,string str2){    if(str1.length()>str2.length()) return 1;    else if(str1.length()<str2.length()) return -1;    else return str1.compare(str2);}//高精度加法//只能是两个正数相加string add(string str1,string str2){    string str;    int len1=str1.length();    int len2=str2.length();    if(len1<len2){        for(int i=1;i<=len2-len1;i++)            str1="0"+str1;    }    else{        for(int i=1;i<=len1-len2;i++)            str2="0"+str2;    }    len1=str1.length();    int cf=0;    int temp;    for(int i=len1-1;i>=0;i--){        temp=str1[i]-'0'+str2[i]-'0'+cf;        cf=temp/10;        temp%=10;        str=char(temp+'0')+str;    }    if(cf) str=char(cf+'0')+str;    return str;}//高精度减法//只能是两个正数相减,而且是大减小string sub(string str1,string str2){    string str;    int tmp=str1.length()-str2.length();    int cf=0;    for(int i=str2.length()-1;i>=0;i--){        if(str1[tmp+i]<str2[i]+cf){            str=char(str1[tmp+i]-str2[i]-cf+'0'+10)+str;            cf=1;        }        else{            str=char(str1[tmp+i]-str2[i]-cf+'0')+str;            cf=0;        }    }    for(int i=tmp-1;i>=0;i--){        if(str1[i]-cf>='0'){            str=char(str1[i]-cf)+str;            cf=0;        }        else{            str=char(str1[i]-cf+10)+str;            cf=1;        }    }    str.erase(0,str.find_first_not_of('0'));    return str;}//高精度乘法//只能是两个正数相乘string mul(string str1,string str2){    string str;    int len1=str1.length();    int len2=str2.length();    string tempstr;    for(int i=len2-1;i>=0;i--){        tempstr="";        int temp=str2[i]-'0';        int t=0;        int cf=0;        if(temp!=0){            for(int j=1;j<=len2-1-i;j++)                tempstr+="0";            for(int j=len1-1;j>=0;j--){                t=(temp*(str1[j]-'0')+cf)%10;                cf=(temp*(str1[j]-'0')+cf)/10;                tempstr=char(t+'0')+tempstr;            }            if(cf!=0) tempstr=char(cf+'0')+tempstr;        }        str=add(str,tempstr);    }    str.erase(0,str.find_first_not_of('0'));    return str;}//高精度除法//两个正数相除,商为quotient,余数为residue//需要高精度减法和乘法void div(string str1,string str2,string "ient,string &residue){    quotient=residue="";    if(str2=="0"){        quotient=residue="ERROR";        return ;    }    if(str1=="0"){        quotient=residue="0";        return ;    }    int res=compare(str1,str2);    if(res<0){        quotient="0";        residue=str1;        return ;    }    else if(res==0){        quotient="1";        residue="0";    }    else{        int len1=str1.length();        int len2=str2.length();        string tempstr;        tempstr.append(str1,0,len2-1);        for(int i=len2-1;i<len1;++i){            tempstr=tempstr+str1[i];            tempstr.erase(0,tempstr.find_first_not_of('0'));            if(tempstr.empty()) tempstr="0";            for(char ch='9';ch>='0';ch--){                string str,tmp;                str=str+ch;                tmp=mul(str2,str);                if(compare(tmp,tempstr)<=0)//试商成功                {                    quotient=quotient+ch;                    tempstr=sub(tempstr,tmp);                    break;                }            }        }        residue=tempstr;    }    quotient.erase(0,quotient.find_first_not_of('0'));    if(quotient.empty()) quotient="0";}string c[2001][2001];//组合数void init(){    for(int i=1;i<=400;i++)        for(int j=1;j<=400;++j)            c[i][j]="0";    c[0][0]="1";    c[1][0]=c[1][1]="1";    for(int i=2;i<=400;++i){        for(int j=0;j<=i;++j){            if(j==0||j==i) c[i][j]="1";            else c[i][j]=add(c[i][j],add(c[i-1][j],c[i-1][j-1]));        }    }}int main(){    #ifdef LOCAL        freopen("read.txt","r",stdin);    #endif // LOCAL    init();//构造组合数    double p,q,ans;    int query;    int m,n;    while(cin>>p>>query&&query)    {        q=1-p;        while(query--)        {            cin>>m>>n;            ans=0;            if(m==0){                if(n==0) ans=-1;                else ans=1;            }            else{            for(int k=1;k<=n;++k){                ans+=atof(c[m-1+n-k][m-1].c_str())*pow(p,m)*pow(q,n-k);            }            }            printf("%.5lf\n",ans);//概率计算完成            //下面计算次数            if(m==0||n==0) cout<<0<<endl;            else cout<<sub(mul(c[m+n][m],"2"),"2")<<endl;        }        cout<<endl;    }    return 0;}

原创粉丝点击