HDU5955 Guessing the Dice Roll(AC自动机+高斯消元)

题目链接
n个人每人选择一个长度为m的序列,序列中每个元素是1到6中的整数。每次投骰子等概率的得到1到6的的一个点数。当点数序列最后m位与任何一个人的序列匹配时停止,且该人获胜,问每个人获胜的概率。

以所有个人的序列构造fail树,那么每个节点有一个编号,且从该点走下一步共有6中情况,每种情况的概率是1/6。当走到终点时,没有下一步,因为游戏已经结束。

假设一共有idx+1(虚拟源点)个点。设走到i的总概率为xi,则 a 0 x 0 + a 1 x 1 + . . . + a i d x x i d x = x i a_0x_0+a_1x_1+...+a_{idx}x_{idx}=x_i a0x0+a1x1+...+aidxxidx=xi(aj为从j点走向i点的概率)。除了虚拟源点外,任何点都不能走向自己,因此上式中 a i = 0 a_i=0 ai=0,将 x i x_i xi移至等式左边, x i x_i xi的系数就是-1,即矩阵中g[i][i]=-1。

对于超级源点(编号为0),初始概率为1.设其他点对它的贡献为
a 0 x 0 + a 1 x 1 + . . . + a i d x x i d x = x 0 − 1 a_0x_0+a_1x_1+...+a_{idx}x_{idx}=x_0-1 a0x0+a1x1+...+aidxxidx=x01 (总概率-初始概率)
移项后a0-1(与其他点一致)等式右边剩下-1
因此最终得到的矩阵如下:
0: a 0 a_0 a0 a 1 a_1 a1 a 2 a_2 a2 a 3 a_3 a3 a i d x a_{idx} aidx  -1
1: a 0 a_0 a0 a 1 a_1 a1 a 2 a_2 a2 a 3 a_3 a3 a i d x a_{idx} aidx   0
2: a 0 a_0 a0 a 1 a_1 a1 a 2 a_2 a2 a 3 a_3 a3 a i d x a_{idx} aidx   0
.
.
.
idx: a 0 a_0 a0 a 1 a_1 a1 a 2 a_2 a2 a 3 a_3 a3 a i d x a_{idx} aidx   0
高斯消元后求得每个点的概率

#include <bits/stdc++.h>

using namespace std;

const int N = 110;
const double eps = 1e-8;

int tr[N][7], n, m, idx;
int ne[N], a[N];
double g[N][N];
bool isend[N];
int id[N], q[N];

int sgn(double a, double b) {
    if (fabs(a - b) < eps)  return 0;
    if (a > b)   return 1;
    else return -1;
}

void ins(int x) {
    int p = 0;
    for (int i = 1; i <= m; i++) {
        int t = a[i];
        if (!tr[p][t])  tr[p][t] = ++ idx;
        p = tr[p][t];
    }
    id[x] = p;
    isend[p] = 1;
}

void build() {
    int hh = 0, tt = -1;
    int cnt = 0;
    for (int i = 1; i <= 6; i ++)
        if (tr[0][i])   q[++ tt] = tr[0][i], g[tr[0][i]][0] += 1.0/6;

    while (hh <= tt) {
        int t = q[hh ++];
        if (isend[t])   continue;
        for (int i = 1; i <= 6; i ++) {
            int &p = tr[t][i];
            if (p) {
                q[++ tt] = p;
                ne[p] = tr[ne[t]][i];
            }
            else   p = tr[ne[t]][i];
            g[p][t] += 1.0/6;
        }
    }
}

void guass() {
    int r = 0, c = 0;
    for (; c <= idx; c ++) {
        int t = r;
        for (int i = t; i <= idx; i ++)
            if (fabs(g[i][c]) > fabs(g[t][c]))t = i;
        if (sgn(g[t][c], 0) == 0)  continue;
        swap(g[t], g[r]);
        for (int i = idx + 1; i >= c; i --) g[r][i] /= g[r][c];

        for (int i = r + 1; i <= idx; i ++) {
            if (sgn(g[i][c], 0) != 0)
                for (int j = idx + 1; j >= c; j --)
                    g[i][j] -= g[r][j] * g[i][c];
        }
        r ++;
    }
    for (int i = idx; i >= 0; i --) {
        for (int j = i + 1; j <= idx; j ++)
            g[i][idx + 1] -= g[i][j] * g[j][idx + 1];
    }
}

int main() {
    int T;
    scanf("%d", &T);
    while (T --) {
        idx = 0;
        memset(tr, 0, sizeof tr);
        memset(ne, 0, sizeof ne);
        memset(g, 0, sizeof g);
        memset(isend, 0, sizeof isend);
        scanf("%d%d", &n, &m);
        for (int i = 1; i <= n; i ++) {
            for (int j = 1; j <= m; j ++)   scanf("%d", &a[j]);
            ins(i);
        }


        for (int i = 0; i <= idx; i ++)
            g[i][i] += -1;//第i个式子移项后xi的系数为-1
        g[0][idx + 1] = -1;
        
        build();

        guass();
        double sum = 0;
        for (int i = 1; i <= n; i ++)   sum += g[id[i]][idx + 1];
        for (int i = 1; i < n; i ++)   printf("%.6f ", g[id[i]][idx + 1] / sum);
        printf("%.6f", g[id[n]][idx + 1] / sum);
        puts("");
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值