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

70 篇文章 0 订阅
23 篇文章 0 订阅

10个人围在一起丢色子,每个人给一个长度为l的序列,色子可以丢无限次,谁的序列先出现谁就胜利,游戏结束。

先说个暴力Y解,先建立AC自动机,然后很容易想到dp[I][J] 第i步走到状态j的概率,然后很明显次数可以用矩阵优化,然而我的姿势怎么也卡不过去。。。。

正解是喜闻乐见的高斯消元,和 游走 一样,先对每个状态建立方程组,然后把初始状态的概率设为1(初始状态即为失配状态)由于一开始由0出发,(很多次后的概率)应该与从其他状态推过来的和为0 再算上初始的概率)所以增广列的第一个值为1 由于前面0状态的系数为-1 所以值改为-1

#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#define eps 1e-6
using namespace std;
int T,n,siz,ans;
int a[500][7],q[5001],fail[5001],vis[5001];
int ed[500],len,s[51];
double an[201][201];
double sqr(double x){return x*x;}
void ins(int x)
{
    int now=0;
    for(int i=1;i<=len;i++)
    {
        int t=s[i];
        if(a[now][t])now=a[now][t];
        else now=a[now][t]=++siz;
    }
    vis[now]++;
    ed[x]=now;
}
void acmach(){
    int head=0,tail=1,k,now;
    q[1]=0;fail[0]=0;
    while(head<tail)
    {
        head++;
        now=q[head];
        for(int i=1;i<=6;i++)
        {
            if(!a[now][i])
            {
                a[now][i]=a[fail[now]][i];
            }
            else
            {
                int temp=a[now][i];
                if(now)  fail[temp]=a[fail[now]][i];
                tail++;  q[tail]=temp;
            }
        }
    }
}


bool gauss(int x)
{
     int now=0,to;double t;
     for(int i=0;i<=x;i++)
     {
         for(to=now;to<=x;to++)if(fabs(an[to][i])>eps)break;
         if(to>x)continue;
         if(to!=now)for(int j=0;j<=x+1;j++)
            swap(an[to][j],an[now][j]);

         t=an[now][i];
         for(int j=0;j<=x+1;j++)
            an[now][j]/=t;

         for(int j=0;j<=x;j++)
            if(j!=now)
            {
                t=an[j][i];
                    for(int k=0;k<=x+1;k++)
                    an[j][k]-=t*an[now][k];
            }
         now++;
     }
}
void solve()
{
    for(int i=0;i<=siz;i++)
    {
        if(vis[i]) continue;

        for(int j=1;j<=6;j++)
        {
            an[a[i][j]][i]+=1.0/6.0;
        }
    }
    for(int i=0;i<=siz;i++)
    {
        an[i][i]+=-1.0;
    }
    an[0][siz+1]=-1.0;
}

void pri()
{
    for(int i=0;i<=siz;i++)
        {
            for(int j=0;j<=siz+1;j++)
            {
                printf("%.2lf ",an[i][j]);
            }
            printf("\n");
        }
}
void init()
{
    memset(a,0,sizeof(a));
    memset(an,0.0,sizeof(an));
    memset(vis,0,sizeof(vis));
    memset(fail,0,sizeof(fail));
}
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        init();
        siz=0;ans=0;
        scanf("%d%d",&n,&len);
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=len;j++) scanf("%d",&s[j]);
            ins(i);
        }
        acmach();
        solve();
        gauss(siz);
        double sum=0.0;
        for(int i=1;i<=n;i++) sum+=an[ed[i]][siz+1];
        for(int i=1;i<n;i++) printf("%.6lf ",an[ed[i]][siz+1]/sum);
        printf("%.6lf\n",an[ed[n]][siz+1]/sum);
    }
    return 0;
}
/*
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
*/
再附上怎么也过不了的矩阵
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#define eps 1e-6
using namespace std;
inline int getint()
{
    int res=0,f=1;char c;
    while(c=getchar(),c<'0'||c>'9'){if(c=='-')f=-1;}
    res=c-'0';
    while(c=getchar(),c>='0'&&c<='9')res=res*10+c-'0';
    return res*f;
}

int T,n,siz,ans;
int a[110][7],q[111],fail[111],vis[111];
int ed[500],len,s[51];
void ins(int x)
{
    int now=0;
    for(int i=1;i<=len;i++)
    {
        int t=s[i];
        if(a[now][t])now=a[now][t];
        else now=a[now][t]=++siz;
    }
    vis[now]++;
    ed[x]=now;
}
void acmach(){
    int head=0,tail=1,k,now;
    q[1]=0;fail[0]=0;
    while(head<tail)
    {
        head++;
        now=q[head];
        for(int i=1;i<=6;i++)
        {
            if(!a[now][i])
            {
                a[now][i]=a[fail[now]][i];
            }
            else
            {
                int temp=a[now][i];
                if(now)  fail[temp]=a[fail[now]][i];
                tail++;  q[tail]=temp;
            }
        }
    }
}

struct matrix
{
    double a[101][101];
    void init()
    {
        memset(a,0,sizeof(a));
    }
    void pri()
    {
        printf("*****************\n");
        for(int i=0;i<=siz*2+1;i++)
        {
            for(int j=0;j<=siz*2+1;j++)
            printf("%lf ",a[i][j]);
            printf("\n");
        }
    }
};
matrix cc1,cc2;
double rt[1000];
matrix tmp1,tmp2;
matrix res;
void mul1()
{
    cc1.init();
    cc2.init();
    for(int i=0;i<=siz;i++)
    {
        for(int j=0;j<=siz;j++)
        {
            //cc1.a[i][j]=0;
            for(int k=0;k<=siz;k++)
            {
                cc1.a[i][j]+=tmp1.a[i][k]*tmp1.a[k][j];
            }
        }
    }

    for(int i=0;i<=siz;i++)
    {
        for(int j=0;j<=siz;j++)
        {
            //cc2.a[i][j]=0;
            for(int k=0;k<=siz;k++)
            {
                cc2.a[i][j]+=tmp1.a[i][k]*tmp2.a[k][j];
            }
            cc2.a[i][j]+=tmp2.a[i][j];
        }
    }
    tmp2=cc2;tmp1=cc1;
}

void solve()
{
    tmp1.init();
    for(int i=0;i<=siz;i++)
    {
        if(vis[i]) continue;
        for(int j=1;j<=6;j++)
        {
            tmp1.a[i][a[i][j]]+=1.0/6.0;
        }
    }
    tmp2=tmp1;
    for(int i=1;i<=20;i++)
    {
        mul1();
    }

    for(int i=1;i<=n;i++) rt[i]=tmp2.a[0][ed[i]];
    double sum=0.0;
        for(int i=1;i<=n;i++) sum+=rt[i];

    for(int i=1;i<n;i++) printf("%.6lf ",rt[i]/sum);
        printf("%.6lf\n",rt[n]/sum);

}
void init()
{
    memset(a,0,sizeof(a));
    memset(vis,0,sizeof(vis));
    memset(fail,0,sizeof(fail));
}
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        init();
        siz=0;
        n=getint();len=getint();
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=len;j++) s[j]=getint();
            ins(i);
        }
        acmach();
        solve();
    }
    return 0;
}
/*
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
*/



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值