zoukankan      html  css  js  c++  java
  • HDU-5955 Guessing the Dice Roll(AC自动机 + 概率DP + 高斯消元)

    题意:这里有N个玩家在玩一个猜测游戏,每个玩家猜测一个由{1, 2, 3, 4, 5, 6}组成的长度为L的序列,那么一个骰子会被不断地投掷直到猜中一个序列,那么这个人就会赢得游戏。

    分析:设(dp[i][j]):表示投掷了i个字符,走到AC自动机的第j个位置的概率,可以知道dp[i][j]可以由前面的6个事件转移过来,即(frac{1}{6} * (dp[i - 1][a1] + dp[i - 1][a2] + dp[i - 1][a3] + ... + dp[i - 1][a6])),a1,a2,a3,a4,a5,a6表示前面的投掷数字在AC自动机的位置,当下一个投掷的数字为k时,会从ac自动机的第ai个位置,跳转到第j个位置。因为跳转会回到原先的状态,因此我们可以使用高斯消元解线性方程组。假设g[i]表示AC自动机第j个位置的概率,我们只需要把不同的节点可以跳转这个位置的系数变为1 / 6即可,即如果一个节点为p,那么下一个投掷的数字为q的话,跳转到(tr[p][q])(令j = tr[p][q]),就令第j行的第q个位置为1/6,然后我们的每个节点即(g[i][i] = 1.0),我们再令根节点(g[0][0] = 1.0)即可,记得用G++交。

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <vector>
    #include <algorithm>
    
    using namespace std;
    const double eps = 1e-10;
    const int N = 10;
    const int M = 500005;
    
    int str[N];
    int tr[M][10], idx;
    int mp[N], has[M];
    int q[M], ne[M];
    int n, l;
    
    double g[N * 10][N * 10];
    
    void insert(int id)
    {
    	int p = 0;
    	for (int i = 1; i <= l; ++i)
    	{
    		int t = str[i];
    		if (!tr[p][t]) tr[p][t] = ++idx;
    		p = tr[p][t];
    	}
    	has[p] = 1;
    	mp[id] = p;
    }
    
    void build()
    {
    	int hh = 0, tt = -1;
    	for (int i = 1; i <= 6; ++i)
    	{
    		if (tr[0][i]) q[++tt] = tr[0][i];
    	}
    
    	while (hh <= tt)
    	{
    		int t = q[hh++];
    
    		for (int i = 1; i <= 6; ++i)
    		{
    			int p = tr[t][i];
    
    			if (!p) tr[t][i] = tr[ne[t]][i];
    			else
    			{
    				ne[p] = tr[ne[t]][i];
    				q[++tt] = p;
    			}
    		}
    	}
    }
    
    int gauss()
    {
    	int c, r;
    	int n = idx + 1;
    	for (c = 0, r = 0; c < n; ++c)
    	{
    		int t = r;
    		for (int i = r; i < n; ++i)
    		{
    			if (fabs(g[i][c]) > fabs(g[t][c]))
    				t = i;
    		}
    
    		if (fabs(g[t][c]) < eps) continue;
    
    		for (int i = c; i <= n; ++i) swap(g[t][i], g[r][i]);
    		for (int i = n; i >= c; --i) g[r][i] /= g[r][c];
    		for (int i = r + 1; i < n; ++i)
    		{
    			if (fabs(g[i][c]) > eps)
    				for (int j = n; j >= c; --j)
    					g[i][j] -= g[r][j] * g[i][c];
    		}
    		++r;
    	}
    
    	if (r < n)
    	{
    		for (int i = r; i < n; ++i)
    			if (fabs(g[i][n]) > eps)
    				return 2;
    		return 1;
    	}
    
    	for (int i = n - 1; i >= 0; --i)
    		for (int j = i + 1; j < n; ++j)
    			g[i][n] -= g[i][j] * g[j][n];
    
    	return 0;
    }
    
    void init()
    {
    	idx = 0;
    	memset(g, 0, sizeof g);
    	memset(ne, 0, sizeof ne);
    	memset(tr, 0, sizeof tr);
    	memset(mp, 0, sizeof mp);
    	memset(has, 0, sizeof has);
    }
    
    int main()
    {
    	int t;
    	scanf("%d", &t);
    
    	while (t--)
    	{		
    		scanf("%d%d", &n, &l);
    
    		for (int i = 1; i <= n; ++i)
    		{
    			for (int j = 1; j <= l; ++j)
    				scanf("%d", &str[j]);
    			insert(i);
    		}
    		
    		build();
    
    		for (int i = 0; i <= idx; ++i) g[i][i] = -1.0;
    		for (int i = 0; i <= idx; ++i)
    		{
    			if (has[i] == 0)
    			{
    				for (int j = 1; j <= 6; ++j)
    				{
    					int p = tr[i][j];
    					g[p][i] += 1.0 / 6.0;
    				}
    			}
    		}
    		g[0][idx + 1] = -1.0;
    
    		gauss();
    
    		for (int i = 1; i <= n; ++i)
    		{
    			printf("%.6lf", g[mp[i]][idx + 1]);
    			if (i == n) printf("
    ");
    			else printf(" ");
    		}
    
    		init();
    	}
    
    	return 0;
    }
    
  • 相关阅读:
    学习笔记::有上下界的网络流
    zoj2314
    bzoj3261
    bzoj 1898
    bzoj4009
    bzoj4033
    bzoj3389
    bzoj2427
    uva 11825
    交换A与B值的四种方法
  • 原文地址:https://www.cnblogs.com/pixel-Teee/p/13269798.html
Copyright © 2011-2022 走看看