HYSBZ 1444 有趣的游戏 AC自动机+概率DP+高斯消元


1444: [Jsoi2009]有趣的游戏

Time Limit: 10 Sec   Memory Limit: 64 MB
Submit: 986   Solved: 325
[ Submit][ Status][ Discuss]

Description

Input

注意 是0<=P

Output

Sample Input


Sample Output


HINT

 30%的数据保证, n ≤ 2. 50%的数据保证, n ≤ 5. 100%的数据保证, n , l, m≤ 10.

Source

[ Submit][ Status][ Discuss]


很容易发现,状态转移出现了环,而且状态不好表示。然后我就不会了...

看了题解才会:

做法是,用ac自动机的next来连接有效状态(只对于字典树中n个人的字母序列中的某个字母),将n个人的“句子”构建成ac自动机,游戏时发生的就是在一个有概率文章中找句子。


之后概率方程是根据自动机中结点间的关系建立的。

nnode为矩阵的行col,nnode+1=var为矩阵的列。

某一行i代表某个结点i是如何从其他节点j转移而来的。

根结点是0。





关于如何构建方程,具体最好是看代码:

根结点很特殊。



#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;

#define all(x) (x).begin(), (x).end()
#define for0(a, n) for (int (a) = 0; (a) < (n); (a)++)
#define for1(a, n) for (int (a) = 1; (a) <= (n); (a)++)
#define mes(a,x,s)  memset(a,x,(s)*sizeof a[0])
#define mem(a,x)  memset(a,x,sizeof a)
#define ysk(x)  (1<<(x))
typedef long long ll;
typedef pair<int, int> pii;
const int INF =0x3f3f3f3f;
const int maxn= 100   ;
const double eps= 1e-10   ;
int n,L,m;
double q[30];
int id[maxn+20];
struct Trie//字典树,ac自动机
{
    int nex[maxn+20][26],fail[maxn+20],end[maxn+20];
    int root,nnode;
    int newnode()
    {
        for0(i,m) nex[nnode][i]=-1;
        end[nnode++]=0;
        return nnode-1;
    }
    void init()
    {
        nnode=0;
        root=newnode();
    }
    void insert(string & buf,int index)
    {
        int len=buf.length();
        int now=root;
        for0(i,len)
        {
            int y=buf[i]-'A';
            if(nex[now][y]==-1)  nex[now][y]=newnode();
            now=nex[now][y ];
        }
        end[now]++;
        id[index]=now;
    }
    void build()//构建AC自动机
    {
        queue<int>Q;
        fail[root]=root;
        for0(i,m)
        {
            if(nex[root][i]==-1)  nex[root][i]=root;
            else
            {
                fail[nex[root][i]]=root;
                Q.push(nex[root][i]);
            }
        }
        while(!Q.empty())
        {
            int now=Q.front();Q.pop();
            for0(i,m)
            {
                if(nex[now][i]==-1)  nex[now][i]=nex[fail[now]][i];
                else
                {
                   fail[nex[now][i] ]=nex[fail[now]][i];
                    Q.push(nex[now][i]);
                }
            }
        }
    }
}trie;
struct Guass
{
    int equ,var;
    double a[maxn+20][maxn+20],x[maxn+10];
    int elimination()//高斯消元
    {
        int i,j,k,col,max_r;
        for(k=0,col=0;k<equ&&col<var;k++,col++)
        {
            max_r=k;
            for(i=k+1;i<equ;i++)
            {
                if(fabs(a[i][col])>fabs(a[max_r][col]))  max_r=i;
            }
            if(fabs(a[max_r][col])<eps )   return 0;
            if(k!=max_r)
            {
                for(j=col;j<=var;j++)  swap(a[k][j],a[max_r][j] );
            }

            for(j=col+1;j<=var;j++) a[k][j]/=a[k][col];
            a[k][col]=1;
            for(i=0;i<equ;i++)  if(i!=k)
            {
                for(j=col+1;j<=var;j++)  a[i][j]-=a[k][j]*a[i][col];
                a[i][col]=0;
            }
        }
        for(i=0;i<equ;i++)
        {
            x[i]=a[i][var];
        }
        return 1;
    }
    void init(int equ,int var)
    {
        this->equ=equ;
        this->var=var;
        mem(a,0);
        for0(i,equ)  a[i][i]=-1;
        a[0][0]=a[0][var]=-1;
        for(int i=0;i<trie.nnode;i++)
        {
            if(trie.end[i])  continue;
             for0(j,m)
             {
                 if(~trie.nex[i][j])
                 {
                     int y=trie.nex[i][j];
                     a[y][i]+=q[j];//同一个点到另一个点,有多种字母可以到达。
                 }
             }
        }
    }

}guass;

int main()
{
   std::ios::sync_with_stdio(false);
   while(cin>>n>>L>>m)
   {
       double x,y;
       for0(i,m)
       {
           cin>>x>>y;
           q[i]=x/y;
       }
        trie.init();
        string s;
        for0(i,n)
        {
          cin>>s;
          trie.insert(s,i);
        }
        trie.build();
        guass.init(trie.nnode,trie.nnode);
        guass.elimination();

       for0(i,n)
       {
          if(x<eps) puts("0.00");
          else printf("%.2f\n",guass.x[id[i]]);
       }

   }
   return 0;
}

/*
3 2 2
1 2
1 2
AB
BA
AA

3 4 2
1 2
1 2
AABA
ABAA
BAAA
*/


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值