bzoj1444 [Jsoi2009]有趣的游戏

  这个题是一道AC自动机上的概率DP,可以帮助理解一下AC自动机的结构。

  首先我们把所有的串插入trie,建立AC自动机,求一下转移函数(相当于构造一个trie图),那么就可以概率DP了。首先到达每个点的概率为变量,然后我们要求的就是到达接受状态(也就是每个串的结尾对应的状态)的概率。注意到了接受状态就不必往下转移了,因为游戏就此结束了。注意有一个限制是必须要加的,就是所有接受状态的概率和是1。如果不加的话,会得到所有点的概率等于0这个答案。那么我们就随便拿一个方程替换这个方程就好了。

  

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<algorithm>
  4 #include<cmath>
  5 #include<cstring>
  6 #define maxn 1200
  7 using namespace std;
  8 int ch[maxn][12],tran[maxn][12],pre[maxn],q[maxn],v[maxn],d[maxn];
  9 char s[maxn];
 10 double rate[maxn],a[maxn][maxn],ans[maxn];
 11 int n,m,len,num,now,rot;
 12 
 13 void insert(int w)
 14 {
 15     if (ch[now][w]==0) ch[now][w]=++num;
 16     now=ch[now][w];
 17 }
 18 
 19 void build()
 20 {
 21     int head=0,tail=1;
 22     q[1]=rot;
 23     while (head<tail)
 24     {
 25         int now=q[++head];
 26         for (int w=1;w<=m;w++)
 27             if (ch[now][w])
 28             {
 29                 int k=ch[now][w];
 30                 if (now==rot) pre[k]=rot;
 31                 else 
 32                 {
 33                     int p=pre[now];
 34                     while (p&&ch[p][w]==0) p=pre[p];
 35                     if (p) pre[k]=ch[p][w];
 36                     else pre[k]=rot;
 37                 }
 38                 q[++tail]=k;
 39             }
 40     }
 41 }
 42 
 43 void gauss(int n)
 44 {
 45     int k=1;
 46     for (int i=1;i<=n;i++)
 47     {
 48         int p=0;
 49         for (int j=k;j<=n;j++)
 50             if (a[j][i]) {    p=j; break;    }
 51         if (!p) continue;
 52         for (int l=1;l<=n+1;l++) swap(a[p][l],a[k][l]);
 53         for (int j=k+1;j<=n;j++)
 54             if (a[j][i])
 55             {
 56                 double tmp=a[j][i]/a[k][i];
 57                 for (int l=1;l<=n+1;l++) a[j][l]-=tmp*a[k][l];
 58             }
 59         k++;
 60     }
 61     for (int i=n;i;i--)
 62     {
 63         ans[i]=a[i][n+1];
 64         for (int j=i+1;j<=n;j++)
 65             ans[i]-=a[i][j]*ans[j];
 66         ans[i]/=a[i][i];
 67     }
 68 }    
 69 
 70 int main()
 71 {
 72     //freopen("game.in","r",stdin);
 73     //freopen("game.out","w",stdout);
 74     scanf("%d%d%d",&n,&len,&m);
 75     int x,y;
 76     for (int i=1;i<=m;i++)
 77     {
 78         scanf("%d%d",&x,&y);
 79         rate[i]=(y)?(1.0*x/y):0;
 80     }
 81     now=rot=num=1;
 82     for (int i=1;i<=n;i++)
 83     {
 84         now=rot;
 85         scanf("%s",s);
 86         for (int j=0;j<len;j++) insert(s[j]-'A'+1);
 87         v[now]=i;
 88         d[i]=now;
 89     }
 90     build();
 91     for (int i=1;i<=num;i++) 
 92         for (int w=1;w<=m;w++)
 93         {
 94             int p=i;
 95             while (p&&ch[p][w]==0) p=pre[p];
 96             if (p) tran[i][w]=ch[p][w];
 97             else tran[i][w]=rot;
 98         }
 99     //求转移函数
100     for (int i=1;i<=num;i++)
101     {
102         a[i][i]=-1;
103         for (int w=1;w<=m;w++)
104             if (!v[i]) a[tran[i][w]][i]+=rate[w];
105     }
106     for (int i=1;i<=num;i++) if (v[i]) a[1][i]=1;else a[1][i]=0;
107     a[1][num+1]=1;
108     gauss(num);
109     for (int i=1;i<=n;i++)
110         if (ans[d[i]]>0&&ans[d[i]]<=1)
111             printf("%.2lf\n",ans[d[i]]);
112         else printf("0.00\n");
113     return 0;
114 }
game

 

  其实直接把trie图构出来会方便很多吧。

转载于:https://www.cnblogs.com/zig-zag/archive/2013/05/15/3080764.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值