• 2014多校第一场J题 || HDU 4870 Rating(DP || 高斯消元)


    题目链接

    题意 :小女孩注册了两个比赛的帐号,初始分值都为0,每做一次比赛如果排名在前两百名,rating涨50,否则降100,告诉你她每次比赛在前两百名的概率p,如果她每次做题都用两个账号中分数低的那个去做,问她最终有一个账号达到1000分需要做的比赛的次数的期望值。

    思路 :可以直接用公式推出来用DP做,也可以列出210个方程组用高斯消元去做。

    (1)DP1:离散化。因为50,100,1000都是50的倍数,所以就看作1,2,20。这样做起来比较方便。

    定义dp[i]为从 i 分数到达i+1分的期望,状态转移方程:

     dp[i] = p+(1-p)*(1+dp[i-2]+dp[i-1]+dp[i]); 在前两百名里增加一分,当不在前两百名里的时候,扣两分,要回到 i+1 分就是1+dp[i-2]+dp[i-1]+dp[i].

    mp[i][i]表示两个账号都从0分涨到 i 分的期望,所以mp[i+1][i] = mp[i][i]+dp[i], mp[i+1][i+1] = mp[i+1][i]+dp[i];

     1 #include <iostream>
     2 #include <stdio.h>
     3 #include <string.h>
     4 #include <math.h>
     5 
     6 using namespace std ;
     7 
     8 double dp[21],mp[21][21] ;
     9 
    10 int main()
    11 {
    12     double p ;
    13     while(scanf("%lf",&p) != EOF)
    14     {
    15         dp[0] = 1 / p ;
    16         dp[1] = 1 / p / p ;
    17         for(int i = 2 ; i < 20 ; i++)
    18             dp[i] = 1 + (1-p)*(dp[i-2]+dp[i-1]+1)/p ;
    19         for(int i = 0 ; i < 20 ; i++)
    20         {
    21             mp[i+1][i] = mp[i][i]+dp[i] ;
    22             mp[i+1][i+1] = mp[i+1][i] + dp[i] ;
    23         }
    24         printf("%.6lf
    ",mp[20][19]) ;
    25     }
    26     return 0 ;
    27 }
    View Code

    (2)DP2:在网上看了一个线性的复杂度的,here,推公式很厉害。

    先考虑一场比赛的情况,定义dp[k]为当前为k分,要达到20分时的期望回合数。

    那么显然有 dp[0]=1+p*dp[1]+q*dp[0] 化简得 dp[0]=1/p+dp[1]

                   dp[1]=1+p*dp[2]+q*dp[0] 化简得 dp[0]=1/p+1/p^2+dp[2]

    我们令  dp[0]=t[k]+dp[k] 那么t[k]就表示由0状态到达k状态所需的期望回合数。如果是要到达20分就是t[20]。

    得到dp[k]=1+p*dp[k+1]+(1-p)*dp[k-2] 

    将dp[k]=dp[0]-t[k]和dp[k-2]=dp[0]-t[k-2]代入得 : 

    dp[0]=1/p+t[k]/p-(1-p)/p*t[k-2]+dp[k+1]  

    然后代入dp[k+1]=dp[0]-t[k+1]得:

    t[k+1]=1/p+1/p*t[k]-(1-p)/p*t[k-2]

    边界条件是  t[0]=0,t[1]=1/p,t[2]=1/p+1/p^2  

    知道这些就可以递推出所有需要的t[k]了。

    因为是两个账号,所以变化一定是(0,0)——>(1,0)——>(1,1)——>……(19,18)——>(19,19)——>(20,19)

    (0,0)->(0,1)需要的期望回合数是t[1]-t[0].  (0,1)->(1,1)需要的期望回合数是 t[1]-t[0]

    (1,1)->(1,2)需要的期望回合数是t[2]-t[1].  (1,2)->(2,2)需要的期望回合数是 t[2]-t[1].

    ....

    (18,18)->(18,19)需要的期望回合数是t[19]-t[18]. (18,19)->(19,19)需要的期望回合数是t[19]-t[18].  

    (19,19)->(19,20)需要的期望回合数是t[20]-t[19]。

    全部加起来的结果就是t[19]*2+t[20]-t[19].

    所以最后的复杂度可以是线性的,而且理论上对于k个账号也是适用的,这样就可以避开了高斯消元的做法了。

     1 #include <iostream>
     2 #include <stdio.h>
     3 #include <string.h>
     4 #include <math.h>
     5 
     6 using namespace std ;
     7 
     8 double d[21] ;
     9 
    10 int main()
    11 {
    12     double p ;
    13     while(scanf("%lf",&p) != EOF)
    14     {
    15         d[0] = 0 ;
    16         d[1] = 1 / p ;
    17         d[2] = 1 / p + 1 / (p * p) ;
    18         for(int i = 3 ; i <= 20 ; i++)
    19             d[i] = 1 / p + d[i-1] / p +(p - 1)*d[i-3] / p ;
    20         printf("%.6lf
    ",d[19] + d[20]) ;
    21     }
    22     return 0 ;
    23 }
    View Code

    (3)高斯消元:

    官方题解:

    令(x, y)表示高分为x,低分为y的状态(x >= y),E(x, y)表示从(x, y)到达(1000, ?)的比赛场数期望。容易得到E(x, y) = P * E(x1, y1) + (1 - P) * E(x2, y2) + 1,其中,(x1, y1)表示rating上升后的状态,(x2, y2)表示rating下降后的状态。每50分一个状态,共有210个状态(21*20/2)。

    移项后得E(x, y) -P * E(x1, y1) - (1 - P) * E(x2, y2) = 1,共有210个这样的方程组。高斯消元求解,x[0]代表E(0,0)这个状态到目标状态的期望。

    注意精度。

     1 #include <stdio.h>
     2 #include <string.h>
     3 #include <iostream>
     4 #include <math.h>
     5 
     6 using namespace std ;
     7 
     8 const double eps = 1e-9 ;
     9 
    10 double p,mp[250][250] ;
    11 int vis[25][25] ;
    12 
    13 double Gauss()
    14 {
    15     for(int i = 0 ; i < 210 ; i++)
    16     {
    17         int k ;
    18         for(k = i ; k < 210 ; k++)
    19             if(fabs(mp[k][i]) > eps) break ;
    20         for(int j = 0 ; j <= 210 ; j++)
    21             swap(mp[i][j],mp[k][j]) ;
    22         for(int j = 0 ; j < 210 ; j++)
    23         {
    24             if(i != j)
    25             {
    26                 if(fabs(mp[j][i]) > eps)
    27                 {
    28                     double s = mp[j][i] / mp[i][i] ;
    29                     for(int k = i ; k <= 210 ; k++)
    30                         mp[j][k] -= mp[i][k] * s ;
    31                 }
    32             }
    33         }
    34     }
    35     return mp[0][210]/mp[0][0]  ;
    36 }
    37 
    38 void pro()
    39 {
    40     for(int i = 0 ; i < 20 ; i++)
    41     {
    42         for(int j = 0 ; j < i ; j ++)
    43         {
    44             int u = vis[i][j] ;
    45             mp[u][u] = 1 ;
    46             mp[u][210] = 1 ;
    47             int v = vis[i][max(0,j-2)] ;
    48             mp[u][v] -= (1-p) ;
    49             v = vis[i][j+1] ;
    50             mp[u][v] -= p ;
    51         }
    52         int u = vis[i][i] ;
    53         mp[u][u] = 1 ;
    54         mp[u][210] = 1 ;
    55         int v = vis[i][max(0,i-2)] ;
    56         mp[u][v] -= (1-p) ;
    57         v = vis[i+1][i] ;
    58         mp[u][v] -= p ;
    59     }
    60 }
    61 
    62 int main()
    63 {
    64 
    65     while(scanf("%lf",&p) != EOF)
    66     {
    67         int cnt = 0 ;
    68         memset(vis,-1,sizeof(vis)) ;
    69         for(int i = 0 ; i < 20  ; i++)
    70             for(int j = 0 ; j <= i ; j++)
    71                 vis[i][j] = cnt ++ ;
    72         memset(mp,0,sizeof(mp)) ;
    73         pro() ;
    74         printf("%.6lf
    ",Gauss()) ;
    75     }
    76     return 0 ;
    77 }
    View Code
  • 相关阅读:
    git 常用命令大全
    iOS UITextView placeHolder占位文字的N种方法实现方法
    ios自定义日期、时间、城市选择器
    zabbix-agent 自定义key
    linux过滤文本
    mysql修改默认存储目录
    git私库搭建
    vmdk,qcow2导入proxmox
    centos字符集问题
    ipset使用
  • 原文地址:https://www.cnblogs.com/luyingfeng/p/3864693.html
Copyright © 2020-2023  润新知