AC自动机+高斯消元求解线性方程--2016icpc沈阳H

Guessing the Dice Roll


Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 462    Accepted Submission(s): 108


Problem Description
There are N players playing a guessing game. Each player guesses a sequence consists of {1,2,3,4,5,6} with length L, then a dice will be rolled again and again and the roll out sequence will be recorded. The player whose guessing sequence first matches the last L rolls of the dice wins the game. 
 

Input
The first line is the number of test cases. For each test case, the first line contains 2 integers N (1 ≤ N ≤ 10) and L (1 ≤ L ≤ 10). Each of the following N lines contains a guessing sequence with length L. It is guaranteed that the guessing sequences are consist of {1,2,3,4,5,6} and all the guessing sequences are distinct.
 

Output
For each test case, output a line containing the winning probability of each player with the precision of 6 digits.
 

Sample Input
  
  
3 5 1 1 2 3 4 5 6 2 1 1 2 1 3 1 4 1 5 1 6 1 4 3 1 2 3 2 3 4 3 4 5 4 5 6
 

Sample Output
  
  
0.200000 0.200000 0.200000 0.200000 0.200000 0.027778 0.194444 0.194444 0.194444 0.194444 0.194444 0.285337 0.237781 0.237781 0.239102
 


问题概述:有n个人每个人先说出一段数子序列(只包含1-6正整数),之后其中一个人不停地roll骰子,当扔出某个

猜的序列时那个人获胜且游戏结束,求每个人获胜的概率


问题转化:

先建棵字典树并跑AC自动机,这样问题就可以转化为:有一个人初始站在字典树的根上,之后他每roll一次骰

会根据骰子的点数走到对应的子节点上,如过当前没有子节点,那就会通过失败指针而走到另一个子节点(或

根)上,直到走个某个叶子结束,求每一片叶子会是终点的概率


思路:

对于上面的问题,我们设pxi为那个人走到节点xi上的概率,那么显然pxi = ∑xk*1.0/6,其中xk为所有可以一步

到达xi的点,那么对于每一个节点都可以列出这样的方程,这样就成为了一个线性方程组,那么问题就可以进一步转

化为求解线性方程组,很显然该方程组一定有解且只有一解,跑一下Gauss就好了,最后输出的所有的pxt,其中t为

叶子节点(但是要注意:对于根节点xr,pxr = ∑xk*1.0/6+1,因为初始条件下人就站在根节点!)

这次因为要求解方程组,所以要化成最简矩阵而不单单是阶梯矩阵,最后化出的矩阵应该是这样:



#include<stdio.h>
#include<string.h>
#include<math.h>
#include<queue>
#include<algorithm>
using namespace std;
#define eps 1e-9	/* 用fabs(k)<eps判断浮点数k是否为0 */
int len, k, ans[15];
double Jz[105][105], c[105];
typedef struct
{
	int loc, now, root, i, net[1005][7], ed[1005], fail[1005];
	void Init()
	{
		loc = 0;
		root = Newnode();
	}
	int Newnode()
	{
		loc += 1;
		for(i=1;i<=6;i++)
			net[loc][i] = -1;
		ed[loc] = 0;
		return loc;
	}
	void Update(int a[])
	{
		now = root;
		for(int i=1;i<=len;i++)
		{
			if(net[now][a[i]]==-1)
				net[now][a[i]] = Newnode();
			now = net[now][a[i]];
		}
		ed[now] = -1;
		ans[++k] = now;
	}
	void Create()
	{
		queue<int> q;
		fail[root] = root;
		for(i=1;i<=6;i++)
		{
			if(net[root][i]==-1)
				net[root][i] = root;
			else
			{
				fail[net[root][i]] = root;
				q.push(net[root][i]);
			}
		}
		while(q.empty()==0)
		{
			now = q.front();
			q.pop();
			for(i=1;i<=6;i++)
			{
				if(net[now][i]==-1)
					net[now][i] = net[fail[now]][i];
				else
				{
					fail[net[now][i]] = net[fail[now]][i];
					q.push(net[now][i]);
				}
			}
		}
	}
}Trie;
Trie AC;
void Gauss(int n, int m);
int main(void)
{
	int T, n, i, j, a[15];
	scanf("%d", &T);
	while(T--)
	{
		k = 0;
		AC.Init();
		scanf("%d%d", &n, &len);
		for(i=1;i<=n;i++)
		{
			for(j=1;j<=len;j++)
				scanf("%d", &a[j]);
			AC.Update(a);
		}
		AC.Create();
		memset(Jz, 0, sizeof(Jz));
		for(i=1;i<=AC.loc;i++)
			Jz[i][i] = -1;
		for(i=1;i<=AC.loc;i++)
		{
			for(j=1;j<=6;j++)
			{
				if(AC.net[i][j]!=-1 && AC.ed[i]!=-1)
					Jz[AC.net[i][j]][i] += 1.0/6;
			}
		}
		memset(c, 0, sizeof(c));
		c[1] = -1;
		Gauss(AC.loc, AC.loc);
		printf("%f", c[ans[1]]);
		for(i=2;i<=n;i++)
			printf(" %f", c[ans[i]]);
		printf("\n");
	}
	return 0;
}

void Gauss(int n, int m)
{
	int i, j, p, q, r;
	p = q = 1;
	while(p<=n && q<=m)
	{
		r = p;
		for(i=p+1;i<=n;i++)
			if(fabs(Jz[i][q])>fabs(Jz[r][q]))  r = i;
		/*if(fabs(Jz[r][q])<eps)
			return 0;  ----方程无解*/
		for(i=q;i<=m;i++)
			swap(Jz[r][i], Jz[p][i]);
		swap(c[r], c[p]);
		c[p] /= Jz[p][q];
		for(i=q+1;i<=m;i++)
			Jz[p][i] /= Jz[p][q];
		for(i=1;i<=n;i++)
		{
			if(Jz[i][q] && i!=p)
			{
				for(j=q+1;j<=m;j++)
					Jz[i][j] -= Jz[p][j]*Jz[i][q];
				c[i] -= c[p]*Jz[i][q];
				Jz[i][q] = 0;
			}
		}
		q++, p++;
	}
	//return 1;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值