4820: [Sdoi2017]硬币游戏

4820: [Sdoi2017]硬币游戏

Time Limit: 10 Sec   Memory Limit: 128 MB
Submit: 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

Sample Output

0.3333333333
0.2500000000
0.4166666667

HINT

Source

[ Submit][ Status][ Discuss]



虽然知道解一定是高斯消元,但是就是不会设变量。。。

定义一个状态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;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值