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

problem

In this particular problem, TheUnreal Tournamentis a tournament, which consists of only two teams. Letthese two teams beAbahoniand Mohamedan. 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 −p that team Mohamedan willwin.

P(i,j) is the probability that teamAbahoniwill win the series given that they still needi more victories to achieve this,whereas teamMohamedanstill needj more 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,iandj and 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 integer N(0 ≤ N < 1001) wherep isthe winning probability ofAbahoni andN 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 1
else if j=0 then return 0
else 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;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值