[SDOI2012]走迷宫

一、题目

点此看题

二、解法

首先判 inf \text{inf} inf 的情况,第一种就是根本到不了终点,第二种就是走到一个岔路口,但是其中一条路到不了终点,那么无论走多少步都没有用,这两个情况还是很好判的。

E ( i ) E(i) E(i)为点 i i i走到终点的期望步数,转移如下( i , j i,j i,j有边相连):
E ( i ) = 1 d e g [ i ] ∑ E ( j ) + 1 E(i)=\frac{1}{deg[i]}\sum E(j)+1 E(i)=deg[i]1E(j)+1这道题的数据直接高斯消元显然不可能,观察数据范围,发现:保证强连通分量的大小不超过 100,我们可以先缩点,这个图就变成了 d a g dag dag,每个点内部是需要高斯消元的,但是 d a g dag dag可以直接转移!我们按拓扑序从大到小处理缩后的点,按上面的转移建立方程组,如果连向自己加入未知元,如果连向外面就可以直接加入常数项了,这么做的时间复杂度是 O ( 1 0 4 n ) O(10^4n) O(104n),贴个代码 q w q qwq qwq

#include <cstdio>
#include <vector>
#include <stack>
using namespace std;
#define eps 1e-8
const int M = 10005;
int read()
{
    int x=0,flag=1;
    char c;
    while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
    while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
    return x*flag;
}
int n,m,k,s,t,tot,Index,cnt,f[M],deg[M],dfn[M],low[M],in[M];
int bel[M],seq[M],arr[M],pos[M];double a[105][105],E[M];
stack<int> st;vector<int> b[M];
struct edge
{
    int v,next;
    edge(int V=0,int N=0) : v(V) , next(N) {}
}e[100*M];
void tarjan(int u)
{
    dfn[u]=low[u]=++Index;
    in[u]=1;st.push(u);
    for(int i=f[u];i;i=e[i].next)
    {
        int v=e[i].v;
        if(!dfn[v])
        {
            tarjan(v);
            low[u]=min(low[u],low[v]);
        }
        else if(in[v])
            low[u]=min(low[u],dfn[v]);
    }
    if(low[u]==dfn[u])
    {
        int v;cnt++;
        do
        {
            v=st.top();st.pop();
            b[cnt].push_back(v);
            bel[v]=cnt;
            in[v]=0;
        }while(v^u);
    }
}
void Gauss(int n)
{
    for(int i=1;i<=n;i++)
    {
        int r=i;
        for(int j=i;j<=n;j++)
            if(a[j][i]-a[r][i]>eps)
                r=j;
        swap(a[i],a[r]);
        for(int j=n+1;j>=i;j--)
            a[i][j]/=a[i][i];
        for(int j=1;j<=n;j++)
        {
            if(i==j || !a[j][i]) continue;
            double tmp=a[j][i]/a[i][i];
            for(int k=i+1;k<=n+1;k++)
                a[j][k]-=a[i][k]*tmp;
            a[j][i]=0;
        }
    }
}
int main()
{
    n=read();m=read();s=read();t=read();
    for(int i=1;i<=m;i++)
    {
        int u=read(),v=read();
        e[++tot]=edge(v,f[u]),f[u]=tot;
        deg[u]++;
    }
    tarjan(s);
    if(!dfn[t]) {puts("INF");return 0;}
    for(int i=1;i<=cnt;i++)
    {
        k=0;
        for(int j=0;j<b[i].size();j++)
            arr[i]|=b[i][j]==t,seq[pos[b[i][j]]=++k]=b[i][j];
        for(int j=0;j<b[i].size();j++)
            for(int l=f[b[i][j]];l;l=e[l].next)
                arr[i]|=arr[bel[e[l].v]];
        if(!arr[i]) {puts("INF");return 0;}
        for(int j=1;j<=k;j++)
            for(int l=1;l<=k+1;l++)
                a[j][l]=0;
        for(int j=0;j<b[i].size();j++)
        {
            int u=b[i][j],p=pos[u];
            a[p][p]=1;
            if(u==t) continue;
            a[p][k+1]=1;
            for(int l=f[u];l;l=e[l].next)
            {
                int v=e[l].v;
                if(bel[v]==i) a[p][pos[v]]-=1.0/deg[u];
                else a[p][k+1]+=1.0/deg[u]*E[v];
            }
        }
        Gauss(k);
        for(int j=1;j<=k;j++) E[seq[j]]=a[j][k+1];
    }
    printf("%.3lf\n",E[s]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值