4820: [Sdoi2017]硬币游戏
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 259 Solved: 106
[ Submit][ Status][ Discuss]
Description
周末同学们非常无聊,有人提议,咱们扔硬币玩吧,谁扔的硬币正面次数多谁胜利。大家纷纷觉得这个游戏非常符
合同学们的特色,但只是扔硬币实在是太单调了。同学们觉得要加强趣味性,所以要找一个同学扔很多很多次硬币
,其他同学记录下正反面情况。用H表示正面朝上,用T表示反面朝上,扔很多次硬币后,会得到一个硬币序列。比
如HTT表示第一次正面朝上,后两次反面朝上。但扔到什么时候停止呢?大家提议,选出n个同学,每个同学猜一个
长度为m的序列,当某一个同学猜的序列在硬币序列中出现时,就不再扔硬币了,并且这个同学胜利,为了保证只
有一个同学胜利,同学们猜的n个序列两两不同。很快,n个同学猜好序列,然后进入了紧张而又刺激的扔硬币环节
。你想知道,如果硬币正反面朝上的概率相同,每个同学胜利的概率是多少。
Input
第一行两个整数n,m。
接下里n行,每行一个长度为m的字符串,表示第i个同学猜的序列。
1<=n,m<=300
Output
输出n行,第i行表示第i个同学胜利的概率。
输出与标准输出的绝对误差不超过10^-6即视为正确。
Sample Input
3 3
THT
TTH
HTT
THT
TTH
HTT
Sample Output
0.3333333333
0.2500000000
0.4166666667
0.2500000000
0.4166666667
HINT
Source
虽然知道解一定是高斯消元,但是就是不会设变量。。。
定义一个状态N,表示任意生成一个串,且没有人能获胜
那么对于N,如果在它后面硬接上小朋友A的串,那么最后的赢家可能就是小朋友A
为什么说可能是呢?考虑下面的情况A = TTH B = HTT
假如对于状态N,它的最后一位是H,那么N + TTH的过程中,加完TT后小朋友B就已经获胜了
这样的情况存在,等价于对于A和B,存在A的某个前缀恰好等于B的某个后缀
于是这时候就能列出关于A的一个式子P(N + A) = P(A) + 0.75P(B)
这个游戏一定会决出一个赢家,于是所有人获胜的概率和应该为1
那么就能列出n + 1个式子,一共n + 1个变量,就能高斯消元了
注意每个串自己和自己也会产生上面的情况
答案的判定是相对误差,因此输出位数不见得越多越好,十位浮点数就行了
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<ctime>
using namespace std;
const int N = 303;
typedef long double DB;
const DB eps = 1E-10;
int n,m,fail[N][N];
DB A[N][N],B[N],mi[N];
char s[N][N];
inline void Swap(int x,int y) {for (int i = x; i <= n; i++) swap(A[x][i],A[y][i]); swap(B[x],B[y]);}
inline DB Check(int x,int y)
{
DB ret = 0; int tot = 0;
for (int i = 1; i <= m; i++)
{
while (tot && s[x][tot + 1] != s[y][i]) tot = fail[x][tot];
if (s[x][tot + 1] == s[y][i]) ++tot;
}
while (tot) ret += mi[m - tot],tot = fail[x][tot];
return ret;
}
inline void Gauss(int k)
{
int pos = k; DB Max = A[k][k],tmp;
for (int i = k + 1; i <= n; i++)
if (fabs(A[i][k]) > Max) Max = fabs(A[i][k]),pos = i;
if (pos > k) Swap(k,pos); tmp = A[k][k];
for (int i = k; i <= n; i++) A[k][i] /= tmp;
B[k] /= tmp; if (k == n) return;
for (int i = k + 1; i <= n; i++)
{
if (fabs(A[i][k]) <= eps) continue;
tmp = A[i][k]; B[i] -= B[k] * tmp;
for (int j = k; j <= n; j++)
A[i][j] -= A[k][j] * tmp;
}
Gauss(k + 1);
for (int i = k + 1; i <= n; i++) B[k] -= B[i] * A[k][i];
}
int main()
{
#ifdef DMC
freopen("DMC.txt","r",stdin);
freopen("test.txt","w",stdout);
#endif
cin >> n >> m; mi[1] = 0.5;
for (int i = 2; i <= m; i++) mi[i] = mi[i - 1] / 2.00;
for (int i = 1; i <= n; i++)
{
scanf("%s",s[i] + 1);
for (int j = 1; j < m; j++)
{
int v = fail[i][j];
while (v && s[i][v + 1] != s[i][j + 1]) v = fail[i][v];
fail[i][j + 1] = s[i][v + 1] == s[i][j + 1] ? v + 1 : 0;
}
}
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
A[i][j] = Check(i,j) + (i == j ? 1.00 : 0.00);
for (int i = 1; i <= n; i++) A[n + 1][i] = 1,A[i][n + 1] = -1;
B[++n] = 1;
/*for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
printf("%.12lf ",(double)(A[i][j]));
printf("%.12lf\n",(double)(B[i]));
}*/
Gauss(1);
for (int i = 1; i < n; i++) printf("%.10lf\n",(double)(B[i]));
return 0;
}