【BZOJ1444】有趣的游戏(JSOI2009)-AC自动机+期望DP+高斯消元

测试地址:有趣的游戏
做法:本题需要用到AC自动机+期望DP+高斯消元。
首先根据题目条件,因为所有串长度相等,并且串各不相同,所以多个玩家不可能同时胜利,而且在AC自动机上一个串的终止节点只有一个,所以我们只需把AC自动机建出来,然后再建状态转移图,那么现在要求的就是,在这个状态转移图上,从AC自动机的根节点出发,走到每一个点的概率是多少。
这种在图上随机游走的问题,我们一般要求走到点i的期望次数(注意不是概率)pi,虽然在终止节点上,这个pi就是概率,但是我们列的式子本质上是期望,不能混淆。根据状态转移图,我们对于每个点列出方程,然后用高斯消元O((nl)3)解出来即可。要注意的是,因为我们指定从AC自动机的根节点开始走,所以需要用我写的BZOJ3143题解中的那种方法理解并处理,另外因为本题中边的概率可能为0,所以有可能所有玩家都不能胜利,这种情况下必须全部输出0,否则高斯消元解出来的全是nan
当然因为本题数据范围不是很大,而且精度要求不是很紧,完全可以迭代很多很多次来求出近似概率,这里我们用的是更加稳妥的方法。
以下是本人代码:

#include <bits/stdc++.h>
using namespace std;
int n,l,m,rt=0,tot=0,fail[110],ch[110][26];
int h,t,q[110],pos[110];
const long double eps=1e-7;
long double p[110],g[110][110]={0};
char s[110];
bool vis[110]={0};

void insert(int &v,int i,int step)
{
    if (!v) v=++tot;
    if (step>=l) {pos[i]=v,vis[v]=1;return;}
    insert(ch[v][s[step]-'A'],i,step+1);
}

void init()
{
    scanf("%d%d%d",&n,&l,&m);
    for(int i=0;i<m;i++)
    {
        long double x,y;
        scanf("%Lf%Lf",&x,&y);
        p[i]=x/y;
    }

    for(int i=1;i<=n;i++)
    {
        scanf("%s",s);
        insert(rt,i,0);
    }
}

void build()
{
    h=t=q[1]=1;
    fail[1]=0;
    while(h<=t)
    {
        int v=q[h++];
        for(int i=0;i<m;i++)
            if (ch[v][i])
            {
                int p=fail[v];
                while(p&&!ch[p][i]) p=fail[p];
                if (p) fail[ch[v][i]]=ch[p][i];
                else fail[ch[v][i]]=1;
                q[++t]=ch[v][i];
            }
    }
}

bool gauss(int n)
{
    for(int i=1;i<=n;i++)
    {
        int mx=i;
        for(int j=i+1;j<=n;j++)
            if (fabs(g[j][i])>fabs(g[mx][i])) mx=j;
        for(int j=i;j<=n+1;j++)
            swap(g[i][j],g[mx][j]);
        if (fabs(g[i][i])<eps) return 0;
        for(int j=i+1;j<=n;j++)
        {
            for(int k=i+1;k<=n+1;k++)
                g[j][k]-=g[j][i]*g[i][k]/g[i][i];
            g[j][i]=0.0;
        }
    }
    for(int i=n;i>=1;i--)
        for(int j=1;j<i;j++)
        {
            g[j][n+1]-=g[j][i]*g[i][n+1]/g[i][i];
            g[j][i]=0.0;
        }
    return 1;
}

void work()
{
    for(int i=1;i<=tot;i++)
        if (!vis[i])
        {
            for(int j=0;j<m;j++)
            {
                int v=i;
                while(v&&!ch[v][j]) v=fail[v];
                if (v) g[ch[v][j]][i]-=p[j];
                else g[1][i]-=p[j];
            }
        }
    for(int i=1;i<=tot;i++)
        g[i][i]+=1.0;
    g[1][tot+1]+=1.0;

    if (!gauss(tot))
    {
        for(int i=1;i<=n;i++)
            printf("0.00\n");
    }
    else
    {
        for(int i=1;i<=n;i++)
            printf("%.2Lf\n",g[pos[i]][tot+1]/g[pos[i]][pos[i]]);
    }
}

int main()
{
    init();
    build();
    work();

    return 0;
}
posted @ 2018-04-06 11:52  Maxwei_wzj  阅读(135)  评论(0编辑  收藏  举报