传送门:【HDU】4870 Rating
题目分析:虽然题目看起来是两个帐号紧密相联的,但是由于每次都是只选择rating低的去打,其实可以将这个过程看成:先将一个号打到19级,然后再将一个号打到20级。
这样我们就列一个状态转移方程:dp[ i ] = p * dp[i + 1] + ( 1 - p ) * dp[i - 2] + 1。
然后做两次高斯消元(一次19,一次20 )即可。注意细节问题。
代码如下:
#include <cmath> #include <cstdio> #include <cstring> #include <algorithm> using namespace std ; #define REP( i , n ) for ( int i = 0 ; i < n ; ++ i ) #define REV( i , n ) for ( int i = n - 1 ; i >= 0 ; -- i ) #define FOR( i , a , b ) for ( int i = a ; i <= b ; ++ i ) #define FOV( i , a , b ) for ( int i = a ; i >= b ; -- i ) #define REPF( i , a , b ) for ( int i = a ; i < b ; ++ i ) #define REPV( i , a , b ) for ( int i = a - 1 ; i >= b ; -- i ) #define CLR( a , x ) memset ( a , x , sizeof a ) const int MAXN = 22 ; const double eps = 1e-10 ; double p ; double a[MAXN][MAXN] ; int sgn ( double x ) { return ( x > eps ) - ( x < -eps ) ; } void guass ( int n , int m ) { for ( int r = 0 , c = 0 ; r <= n , c <= m ; ++ r , ++ c ) { int tmp = r ; FOR ( i , r + 1 , n ) if ( sgn ( fabs ( a[i][c] ) - fabs ( a[tmp][c] ) ) > 0 ) tmp = i ; if ( sgn ( a[tmp][c] ) == 0 ) { --r ; continue ; } if ( tmp != r ) FOR ( i , c , m ) swap ( a[r][i] , a[tmp][i] ) ; FOV ( i , m , c ) a[r][i] /= a[r][c] ; FOR ( i , 0 , n ) if ( sgn ( a[i][c] ) && i != r ) FOV ( j , m , c ) a[i][j] -= a[i][c] * a[r][j] ; } } double solve ( int n ) { CLR ( a , 0 ) ; FOR ( i , 0 , n ) { if ( i == 0 ) { a[i][i] = p ; a[i][i + 1] = -p ; a[i][n + 1] = 1 ; } else if ( i == 1 ) { a[i][i] = 1 ; a[i][i - 1] = p - 1 ; a[i][i + 1] = -p ; a[i][n + 1] = 1 ; } else if ( i != n ) { a[i][i] = 1 ; a[i][i + 1] = -p ; a[i][i - 2] = p - 1 ; a[i][n + 1] = 1 ; } else { a[i][i] = 1 ; } } guass ( n , n + 1 ) ; return a[0][n + 1] ; } int main () { while ( ~scanf ( "%lf" , &p ) ) printf ( "%.6f\n" , solve ( 19 ) + solve ( 20 ) ) ; return 0 ; }