1. 程式人生 > >NKOJ 4087 (SDOI 2017)硬幣遊戲(高斯消元)

NKOJ 4087 (SDOI 2017)硬幣遊戲(高斯消元)

P4087【SDOI2017】硬幣遊戲

問題描述

這裡寫圖片描述

輸入格式

這裡寫圖片描述

輸出格式

這裡寫圖片描述

樣例輸入

3 3
THT
TTH
HTT

樣例輸出

0.3333333333
0.2500000000
0.4166666667

提示

這裡寫圖片描述

這題顯然的想到高斯消元,關鍵是如何建立方程。
不妨設每個人贏的概率分別為P1,P2...Pn,那麼顯然我們需要尋找他們之間的關係。
此題的關鍵在於假設一個狀態N,他表示所有的沒有人獲勝的狀態。

舉個例子來說明,就以樣例為例。
這三個人分別猜的是THT,TTH,HTT
考慮第一個人勝利的概率,那麼如果在N

後面接上一個THT,顯然第一個人就勝利了。那麼P1=18PN。這表示N後面三次扔硬幣恰好是THT的概率。

但是顯然這樣不對,因為如果N的結尾是在T,那麼扔出TH時,第二個人就已經贏了,也有可能結尾是TH,那麼在扔出T時,第一個人已經贏了。需要把這些情況發生的概率減掉。

那麼應該有P1=18PN14P112P214P3
對應的係數就是對應串出現的概率。實際上,當某個串的一個字尾等於當前串的一個字首時,就會對當前串的概率造成影響。

由於此題資料較弱,可以暴力匹配。當然也可以用KMP。

程式碼:

#include<stdio.h>
#include<iostream>
#include<algorithm> #include<cstring> #define N 1005 using namespace std; int n,m; char S[N][N]; double A[N][N],B[N],X[N]; void Gauss(int row,int col) { int i,j,x,y,MR;double t,tmp; for(x=1,y=1,MR=1;x<=row&&y<col;x++,y++,MR=x) { for(i=x+1;i<=row;i++)if
(abs(A[i][y])>abs(A[MR][y]))MR=i; if(i!=x)for(i=1;i<=col;i++)swap(A[x][i],A[MR][i]); if(!A[x][y]){x--;continue;} for(i=x+1;i<=row;i++) if(A[i][y]) { t=A[i][y]/A[x][y]; for(j=y;j<=col;j++)A[i][j]-=A[x][j]*t; } } for(i=row;i>=1;i--) { tmp=A[i][col]; for(j=i+1;j<col;j++)tmp-=X[j]*A[i][j]; X[i]=tmp/A[i][i]; } for(i=1;i<=n;i++)printf("%.10lf\n",X[i]); } int main() { int i,j,k,p,t; scanf("%d%d",&n,&m);B[0]=1.0; for(i=1;i<=n;i++)scanf("%s",S[i]),A[i][i]=1.0; for(i=1;i<=m;i++)B[i]=B[i-1]/2; for(i=1;i<=n;i++) { A[i][n+1]=-1.0; for(j=1;j<=n;j++) for(k=1,t=0;k<m;k++,t=0) { while(k+t<m&&S[j][k+t]==S[i][t])t++; if(k+t==m)A[i][j]+=B[k]; } } for(i=1;i<=n;i++)A[n+1][i]=1.0;A[n+1][n+2]=1.0; Gauss(n+1,n+2); }