hdu4870 Rating (高斯消元or dp)

Rating

Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 1246    Accepted Submission(s): 762
Special Judge

Problem Description
A little girl loves programming competition very much. Recently, she has found a new kind of programming competition named "TopTopTopCoder". Every user who has registered in "TopTopTopCoder" system will have a rating, and the initial value of rating equals to zero. After the user participates in the contest held by "TopTopTopCoder", her/his rating will be updated depending on her/his rank. Supposing that her/his current rating is X, if her/his rank is between on 1-200 after contest, her/his rating will be min(X+50,1000). Her/His rating will be max(X-100,0) otherwise. To reach 1000 points as soon as possible, this little girl registered two accounts. She uses the account with less rating in each contest. The possibility of her rank between on 1 - 200 is P for every contest. Can you tell her how many contests she needs to participate in to make one of her account ratings reach 1000 points?
 

Input
There are several test cases. Each test case is a single line containing a float number P (0.3 <= P <= 1.0). The meaning of P is described above.
 

Output
You should output a float number for each test case, indicating the expected count of contest she needs to participate in. This problem is special judged. The relative error less than 1e-5 will be accepted.
 

Sample Input
1.000000

0.814700

 
Sample Output
39.000000

82.181160

 
Author
FZU

 

题意:给定一个概率p,做一道题有p概率成功,(1-p)概率失败。成功分数+50,最高1000;失败分数-100,最低0分。现有两个账号,每次选择分数低的账号去做题,问其中一个账号达到1000分的期望次数。

思路:想了很久这题,也看了不少解题报告,总之有两大类解法。

1.DP

因为每次50分,到达1000分,所以可以看做每次1分,到达20分
dp[i]表示i到20的数学期望
那么dp[i] = dp[i+1]*p+dp[i-2]*q+1;
令t[i] = dp[i+1]-dp[i]         t[i]表示转化的期望
则t[i] = (t[i+1]*p+t[i-2]*q)       这个自己等式相减可得
所以t[i+1] = (t[i]-t[i-2]*q)/p

ans=∑t[i]*2-g[19](0<=i<=19) 

代码
#include <stdio.h>
int main()
{
    float p,sum,t[21],q;
    int i;
    while(~scanf("%f",&p))
    {
        sum = 0;
        q = 1-p;
        t[0] = 1/p,t[1] = t[0]/p,t[2] = t[1]/p;
        sum = t[0]+t[1]+t[2];
        for(i = 3;i<20;i++)
        {
            t[i] = (t[i-1]-t[i-3]*q)/p;
            sum+=t[i];
        }
        printf("%.6f\n",sum*2-t[19]);
    }
    return 0;
}
View Code

2.高斯消元(有两种建立方程的方法)

(1)

令E(X,Y)为账号分数为x,y打到1000的数学期望。则有:
E(X,Y)=PE(X1,Y1)+(1-P)E(X2,Y2)+1,X1,Y1是XY分数赢了的分数,X2,Y2对应是输了的分数,假设X>=Y,每次比赛用Y的账号,就会有210条方程,用id数组标记XY分数对应的系数的索引。

kuangbin的代码:

/* ***********************************************
Author        :kuangbin
Created Time  :2014/7/22 23:33:49
File Name     :E:\2014ACM\比赛\2014多校训练\2014多校1\HDU4870.cpp
************************************************ 
*/

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <math.h>
#include <stdlib.h>
#include <time.h>
using namespace std;

#define eps 1e-9
const int MAXN=420;
double a[MAXN][MAXN],x[MAXN];//方程的左边的矩阵和等式右边的值,求解之后x存的就是结果
int equ,var;//方程数和未知数个数
/*

*返回0表示无解,1表示有解
*/
int Gauss()
{
    int i,j,k,col,max_r;
    for(k=0,col=0;k<equ&&col<var;k++,col++)
    {
        max_r=k;
        for(i=k+1;i<equ;i++)
          if(fabs(a[i][col])>fabs(a[max_r][col]))
            max_r=i;
      //  if(fabs(a[max_r][col])<eps)return 0;
        if(k!=max_r)
        {
            for(j=col;j<var;j++)
              swap(a[k][j],a[max_r][j]);
            swap(x[k],x[max_r]);
        }
        x[k]/=a[k][col];
        for(j=col+1;j<var;j++)a[k][j]/=a[k][col];
        a[k][col]=1;
        for(i=0;i<equ;i++)
          if(i!=k)
          {
              x[i]-=x[k]*a[i][col];
              for(j=col+1;j<var;j++)a[i][j]-=a[k][j]*a[i][col];
              a[i][col]=0;
          }
    }
    return 1;
}

int id[22][22];

int main()
{
    //freopen("1010.in","r",stdin);
    
//freopen("out.txt","w",stdout);
    int cnt = 0;
    for(int i = 0;i <= 20;i++)
        for(int j = 0;j <= 20;j++)
        {
            if(i > j)continue;
            if(i == 20 && j == 20)continue;
            id[i][j] = cnt++;
        }
    equ = var = cnt;
    double p;
    while(scanf("%lf",&p) == 1)
    {
        memset(a,0,sizeof(a));
        for(int i = 0;i <= 20;i++)
            for(int j = 0;j <= 20;j++)
            {
                if(i > j)continue;
                if(i == 20 && j == 20)continue;
                int u = id[i][j];
                a[u][u] = 1.0;
                if(i == 20 || j == 20)
                {
                    x[u] = 0.0;
                    continue;
                }
                x[u] = 1.0;
                int nx = i+1;
                if(nx <= j)
                    a[u][id[nx][j]] -= p;
                else a[u][id[j][nx]] -= p;
                nx = max(0,i-2);
                a[u][id[nx][j]] -= (1-p);
            }
        Gauss();
        printf("%.6lf\n",x[0]);
    }
    return 0;
}
View Code

(2)

得分必须是f(0,0)->f(0,50)->f(50,50)->...->f(950,950)->f(950,1000)。所以所求期望次数就是到一个账号到1000的期望次数加上另一个账号达到950分的期望次数。记f(i)表示分数i*50到1000分需要的期望次数。那么答案ans=f(0)*2-f(19)。也就是说两次都到1000分,然后减去一次950到1000分的期望次数。
我们可以列方程,对于i>=2的数来说,我们记xi=f(i),那么可以得到方程组xi=p*(x(i+1)+1)+(1-p)*(x(i-2)+1)。其中(0<=i<20,i为0时需要特别处理,x20=0)。这样我们就可以直接套高斯消元法的模板了。

我用bin神板子敲了遍

代码:

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <math.h>
#include <stdlib.h>
#include <time.h>
using namespace std;

#define eps 1e-9
const int MAXN=420;
double a[MAXN][MAXN],x[MAXN];//方程的左边的矩阵和等式右边的值,求解之后x存的就是结果
int equ,var;//方程数和未知数个数
/*

*返回0表示无解,1表示有解
*/
int Gauss()
{
    int i,j,k,col,max_r;
    for(k=0,col=0; k<equ&&col<var; k++,col++)
    {
        max_r=k;
        for(i=k+1; i<equ; i++)
            if(fabs(a[i][col])>fabs(a[max_r][col]))
                max_r=i;
        //  if(fabs(a[max_r][col])<eps)return 0;
        if(k!=max_r)
        {
            for(j=col; j<var; j++)
                swap(a[k][j],a[max_r][j]);
            swap(x[k],x[max_r]);
        }
        x[k]/=a[k][col];
        for(j=col+1; j<var; j++)a[k][j]/=a[k][col];
        a[k][col]=1;
        for(i=0; i<equ; i++)
            if(i!=k)
            {
                x[i]-=x[k]*a[i][col];
                for(j=col+1; j<var; j++)a[i][j]-=a[k][j]*a[i][col];
                a[i][col]=0;
            }
    }
    return 1;
}

int id[22][22];

int main()
{
    equ = var = 21;
    double p;
    while(scanf("%lf",&p) == 1)
    {
        memset(a,0,sizeof(a));
        for(int i = 0; i <= 20; i++)
        {
            a[i][i] = 1.0;
            if(i==20)
            {
                x[i] = 0;
                continue;
            }
            x[i] = 1.0;
            if(i==0)
            {
                a[i][i] = p;
                a[i][i+1] = -p;
                continue;
            }
            int nx = i-2;
            nx = max(nx , 0);
            a[i][i+1] = -p;
            a[i][nx] = p - 1;
        }
        Gauss();
        printf("%.6lf\n",x[0]*2 - x[19]);
    }
    return 0;
}
View Code
posted @ 2015-07-21 10:39  Doli  阅读(110)  评论(0编辑  收藏  举报