• 【BZOJ2707】走迷宫(SDOI2012)-SCC缩点+拓扑排序+期望DP+高斯消元


    测试地址:走迷宫
    题目大意:有一个n个点的有向图,人从起点S出发,每次等概率随机选择一条出边走出,问走到终点T的期望步数。n104,一个强连通分量中的点数不超过100
    做法:本题需要用到SCC缩点+拓扑排序+期望DP+高斯消元。
    我们很快就能根据标准的期望逆推型DP得到该题的状态转移方程,因为图中存在环,所以需要高斯消元,然而O(n3)的时间复杂度显然会炸。这时数据范围里的提示已经剧透了做法的一部分:强连通分量。
    注意到把强连通分量缩起来后,整个图是一个DAG,而DAG上的点的期望只跟它出边连向的点有关。那么我们对于两个强连通分量之间的边,就直接算出它们的贡献,而对于一个强连通分量中的点,用高斯消元即可。
    还值得注意的一点是INF的判断,如果从起点根本走不到终点,或者能从起点走到一个走不到终点的点,那么期望步数就是INF。
    以上算法的时间复杂度为O(nss3)=O(ns2)s为强连通分量的大小),因为高斯消元常数很小,所以可以通过此题。
    我傻逼的地方:我居然把Tarjan算法求SCC写错了……下个礼拜就省选了,怕不是要凉了……
    以下是本人代码:

    #include <bits/stdc++.h>
    using namespace std;
    int n,m,S,T,a[1000010],b[1000010],first[20010]={0},tot=0;
    int top,st[20010],dfn[20010],low[20010],tim=0,belong[20010]={0},scc;
    int h,t,q[20010],totp=0,pos[20010],qpos[20010],l[20010],r[20010],in[20010]={0},sccout[20010]={0};
    bool vis[20010]={0},inst[20010]={0};
    long double out[20010]={0},p[20010],g[210][210];
    struct edge
    {
        int v,next;
    }e[2000010];
    
    void insert(int a,int b)
    {
        e[++tot].v=b;
        e[tot].next=first[a];
        first[a]=tot;
    }
    
    void init()
    {
        scanf("%d%d%d%d",&n,&m,&S,&T); 
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d",&a[i],&b[i]);
            insert(a[i],b[i]);
            out[a[i]]+=1.0; 
        }
        scc=n;
    }
    
    void tarjan(int v)
    {
        dfn[v]=low[v]=++tim;
        st[++top]=v;
        vis[v]=inst[v]=1;
        int now=top;
        for(int i=first[v];i;i=e[i].next)
        {
            if (!vis[e[i].v])
            {
                tarjan(e[i].v);
                low[v]=min(low[v],low[e[i].v]);
            }
            else if (inst[e[i].v]) low[v]=min(low[v],dfn[e[i].v]);
        }
        if (dfn[v]==low[v])
        {
            scc++;
            l[scc]=totp+1;
            while(top>=now)
            {
                belong[st[top]]=scc;
                pos[++totp]=st[top];
                inst[st[top]]=0;
                top--;
            }
            r[scc]=totp;
        }
    }
    
    void toposort()
    {
        h=t=1;
        for(int i=1;i<=m;i++)
            if (belong[a[i]]&&belong[b[i]]&&belong[a[i]]!=belong[b[i]])
                insert(belong[a[i]],belong[b[i]]),in[belong[b[i]]]++,sccout[belong[a[i]]]++;
        q[1]=belong[S];
        while(h<=t)
        {
            int v=q[h++];
            for(int i=first[v];i;i=e[i].next)
            {
                in[e[i].v]--;
                if (!in[e[i].v]) q[++t]=e[i].v;
            }
        }
    }
    
    void gauss(int n)
    {
        for(int i=1;i<=n;i++)
        {
            int mx=i;
            for(int j=i+1;j<=n;j++)
                if (fabs(g[j][i])>fabs(g[mx][i])) mx=j;
            for(int j=i;j<=n+1;j++)
                swap(g[i][j],g[mx][j]);
            for(int j=i+1;j<=n;j++)
            {
                for(int k=i+1;k<=n+1;k++)
                    g[j][k]-=g[j][i]*g[i][k]/g[i][i];
                g[j][i]=0.0;
            }
        }
        for(int i=n;i>=1;i--)
            for(int j=1;j<i;j++)
            {
                g[j][n+1]-=g[j][i]*g[i][n+1]/g[i][i];
                g[j][i]=0.0;
            }
    }
    
    void work()
    {
        for(int i=n+1;i<=scc;i++)
            if (i!=belong[T]&&!sccout[i]) {printf("INF");return;}
        p[T]=0.0;
        for(int i=t;i>=1;i--)
        {
            int v=q[i];
            memset(g,0,sizeof(g));
            for(int j=l[v];j<=r[v];j++)
                qpos[pos[j]]=j-l[v]+1;
            for(int j=l[v];j<=r[v];j++)
            {
                int x=pos[j];
                if (x==T) {g[qpos[x]][qpos[x]]+=1.0;continue;}
                for(int k=first[x];k;k=e[k].next)
                {
                    int y=e[k].v;
                    if (y==T||belong[e[k].v]!=v) g[qpos[x]][r[v]-l[v]+2]+=p[y]+1.0;
                    else
                    {
                        g[qpos[x]][qpos[y]]-=1.0;
                        g[qpos[x]][r[v]-l[v]+2]+=1.0;
                    }
                }
                for(int k=1;k<=r[v]-l[v]+2;k++)
                    g[qpos[x]][k]/=out[x];
                g[qpos[x]][qpos[x]]+=1.0;
            }
            gauss(r[v]-l[v]+1);
            for(int j=l[v];j<=r[v];j++)
                p[pos[j]]=g[j-l[v]+1][r[v]-l[v]+2]/g[j-l[v]+1][j-l[v]+1];
        }
        printf("%.3Lf",p[S]);
    }
    
    int main()
    {
        init();
        tarjan(S);
        toposort();
        work();
    
        return 0;
    }
  • 相关阅读:
    转载 linux 僵尸进程,讲的很透彻
    linux 程序自动运行总结
    使用__FILE__和__LINE__定位错误
    CURL简单使用
    阿里云域名绑定IP
    使用PHPEXCEL导入数据到数据库
    关于使用ueditor时候遇到的情况
    TP5使用PHPMAILER发送邮件
    使用ajax,结合jquery,php实现图片上传预览功能
    mysql关于数据量大的时候分页分批处理
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793479.html
Copyright © 2020-2023  润新知