zoukankan      html  css  js  c++  java
  • 【BZOJ2707】[SDOI2012]走迷宫 Tarjan+拓扑排序+高斯消元+期望

    【BZOJ2707】[SDOI2012]走迷宫

    Description

    Morenan被困在了一个迷宫里。迷宫可以视为N个点M条边的有向图,其中Morenan处于起点S,迷宫的终点设为T。可惜的是,Morenan非常的脑小,他只会从一个点出发随机沿着一条从该点出发的有向边,到达另一个点。这样,Morenan走的步数可能很长,也可能是无限,更可能到不了终点。若到不了终点,则步数视为无穷大。但你必须想方设法求出Morenan所走步数的期望值。

    Input

    第1行4个整数,N,M,S,T
    第[2, M+1]行每行两个整数o1, o2,表示有一条从o1到o2的边。

    Output

    一个浮点数,保留小数点3位,为步数的期望值。若期望值为无穷大,则输出"INF"。
    【样例输入1】
    6 6 1 6
    1 2
    1 3
    2 4
    3 5
    4 6
    5 6
    【样例输出1】
    3.000
    【样例输入2】
    9 12 1 9
    1 2
    2 3
    3 1
    3 4
    3 7
    4 5
    5 6
    6 4
    6 7
    7 8
    8 9
    9 7
    【样例输出2】
    9.500
    【样例输入3】
    2 0 1 2
    【样例输出3】
    INF
    【数据范围】
    测试点
    N
    M
    Hint
    [1, 6]
    <=10
    <=100
     
    [7, 12]
    <=200
    <=10000
     
    [13, 20]
    <=10000
    <=1000000
    保证强连通分量的大小不超过100
     
     
    另外,均匀分布着40%的数据,图中没有环,也没有自环

    题解:先Tarjan缩环,然后对于强连通分量内部的点,用高斯消元,外部的DAG用拓扑排序+DP。具体细节还是说一下吧:
    1.高斯消元时,列方程$f[i]=sum f[j]/d[i]+1$,发现i的某些出边可能指向强连通分量外的点,把它们都当成常数项就好了。特别地,T的方程要特殊处理。
    2.判断INF时比较坑,从S开始BFS,我们的DAG只能包含我们能DFS到的点,并且,搜到T时要立刻停止。DFS后,如果发现一个强连通分量的出度为0且不是T所在的强连通分量,则INF。

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <vector>
    #include <queue>
    #include <cmath>
    const int maxn=10010;
    using namespace std;
    int n,m,cnt,Cnt,tot,top,sum,S,T;
    vector<int> s[maxn];
    int dep[maxn],low[maxn],head[maxn],to[1000010],next[1000010],sta[maxn],d[maxn],ins[maxn],D[maxn],pos[maxn],bel[maxn];
    int To[1000010],Next[1000010],Head[maxn],vis[maxn];
    double p[maxn],v[110][110];
    queue<int> q;
    inline void add(int a,int b)
    {
    	to[cnt]=b,next[cnt]=head[a],head[a]=cnt++;
    }
    inline void Add(int a,int b)
    {
    	To[Cnt]=b,Next[Cnt]=Head[a],Head[a]=Cnt++;
    }
    void tarjan(int x)
    {
    	dep[x]=low[x]=++tot,sta[++top]=x,ins[x]=1;
    	int i,t;
    	for(i=Head[x];i!=-1;i=Next[i])
    	{
    		if(!dep[To[i]])	tarjan(To[i]),low[x]=min(low[x],low[To[i]]);
    		else	if(ins[To[i]])	low[x]=min(low[x],dep[To[i]]);
    	}
    	if(dep[x]==low[x])
    	{
    		sum++;
    		do
    		{
    			t=sta[top--],ins[t]=0,bel[t]=sum,pos[t]=s[sum].size(),s[sum].push_back(t);
    		}while(t!=x);
    	}
    }
    void calc(int x)
    {
    	int i,j,k,a,b,nm=s[x].size();
    	for(i=0;i<nm;i++)	memset(v[i],0,sizeof(v[0][0])*(nm+1));
    	for(i=0;i<nm;i++)
    	{
    		a=s[x][i];
    		for(j=head[a];j!=-1;j=next[j])
    		{
    			b=to[j];
    			if(bel[b]==bel[a])	v[pos[b]][pos[a]]+=1.0/d[b],v[pos[b]][nm]-=1.0/d[b];
    		}
    	}
    	for(i=0;i<nm;i++)
    	{
    		v[i][nm]-=p[s[x][i]];
    		if(s[x][i]==T)	for(j=0;j<=nm;j++)	v[i][j]=0;
    		v[i][i]-=1;
    	}
    	for(i=0;i<nm;i++)
    	{
    		for(j=i+1;j<nm;j++)	if(fabs(v[j][i])>fabs(v[i][i]))	for(k=i;k<=nm;k++)	swap(v[j][k],v[i][k]);
    		double tmp=v[i][i];
    		if(fabs(tmp)<1e-9)	continue;
    		for(k=i;k<=nm;k++)	v[i][k]/=tmp;
    		for(j=0;j<nm;j++)	if(i!=j)
    		{
    			tmp=v[j][i];
    			for(k=i;k<=nm;k++)	v[j][k]-=v[i][k]*tmp;
    		}
    	}
    	for(i=0;i<nm;i++)	p[s[x][i]]=v[i][nm];
    }
    void dfs(int x)
    {
    	vis[x]=1;
    	if(x==T)	return ;
    	for(int i=Head[x];i!=-1;i=Next[i])
    	{
    		if(bel[To[i]]!=bel[x])	D[bel[x]]++;
    		if(!vis[To[i]])	dfs(To[i]);
    	}
    }
    inline int rd()
    {
    	int ret=0,f=1;	char gc=getchar();
    	while(gc<'0'||gc>'9')	{if(gc=='-')f=-f;	gc=getchar();}
    	while(gc>='0'&&gc<='9')	ret=ret*10+gc-'0',gc=getchar();
    	return ret*f;
    }
    int main()
    {
    	n=rd(),m=rd(),S=rd(),T=rd();
    	int i,j,a,b,u,v;
    	memset(head,-1,sizeof(head)),memset(Head,-1,sizeof(Head));
    	for(i=1;i<=m;i++)	a=rd(),b=rd(),Add(a,b),add(b,a),d[a]++;
    	for(i=1;i<=n;i++)	if(!dep[i])	tarjan(i);
    	dfs(S);
    	for(i=1;i<=n;i++)	if(vis[i]&&bel[i]!=bel[T]&&!D[bel[i]])
    	{
    		printf("INF");
    		return 0;
    	}
    	q.push(bel[T]);
    	while(!q.empty())
    	{
    		u=q.front(),q.pop();
    		calc(u);
    		if(u==bel[S])
    		{
    			printf("%.3lf",fabs(p[S]));
    			return 0;
    		}
    		for(i=0;i<(int)s[u].size();i++)	for(v=s[u][i],j=head[v];j!=-1;j=next[j])	if(bel[to[j]]!=bel[v])
    		{
    			D[bel[to[j]]]--,p[to[j]]+=(p[v]+1)/d[to[j]];
    			if(!D[bel[to[j]]])	q.push(bel[to[j]]);
    		}
    	}
    	printf("INF");
    	return 0;
    }//9 12 1 6 1 2 2 3 3 1 3 4 3 7 4 5 5 6 6 4 6 7 7 8 8 9 9 7
  • 相关阅读:
    对初学者的几点建议
    关于.net的一些资源网站
    iis 经常出现的问题以及解决方案
    C#编程规范(2008年4月新版)
    优秀ASP.NET程序员的修炼之路
    网站软件开发规范(某门户网站的)
    asp.net跳转页面的三种方法比较
    H5开发相关资料
    C#简介
    用C#获取硬盘序列号,CPU序列号,网卡MAC地址
  • 原文地址:https://www.cnblogs.com/CQzhangyu/p/7561383.html
Copyright © 2011-2022 走看看