zoukankan      html  css  js  c++  java
  • [SDOI2012]走迷宫

    X.[SDOI2012]走迷宫

    这题本来是一个SCC+高斯消元的模板题来着的……但关键是DP状态的设计。

    首先先判一下无解。显然,如果从起点出发能够走到一个走不到终点的点,则为无解。这很好想——只要答案有为无穷大的可能,无论概率多小,最终答案都会为无穷大。

    然后就是DP设计了。我们无论设什么从起点出发的状态都是不太好的——所以,我们应该反过来,设从终点出发的状态。即,设\(f_x\)表示从节点\(x\)出发到终点的期望长度,则有

    \[f_x=\dfrac{\sum\limits_{(x,y)\in\mathbb{E}}f_y}{deg_x}+1 \]

    因为我们发现在不同SCC间转移是有序的,就可以直接按照拓扑序DP;只有在同一个SCC内部才会出现环,于是有环就直接用高斯消元暴力消掉即可。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    int n,m,S,T,col[10100],in[10100],ord[10100],out[10100],sz[10100],c;
    vector<int>ele[10100];
    namespace SCC{
    	int dfn[10100],low[10100],tot;
    	stack<int>stk;
    	vector<int>v[10100],u[10100];
    	bool vis1[10100],vis2[10100];
    	void dfs1(int x){
    		vis1[x]=true;
    		for(auto y:u[x])if(!vis1[y])dfs1(y);
    	}
    	void dfs2(int x){
    		if(!vis1[x]){puts("INF");exit(0);}
    		vis2[x]=true;
    		for(auto y:v[x])if(!vis2[y])dfs2(y);
    	}
    	void Tarjan(int x){
    		dfn[x]=low[x]=++tot,stk.push(x);
    		for(auto y:v[x]){
    			if(!dfn[y])Tarjan(y),low[x]=min(low[x],low[y]);
    			else if(!col[y])low[x]=min(low[x],dfn[y]);
    		}
    		if(dfn[x]!=low[x])return;
    		c++;
    		int y;
    		do{y=stk.top(),col[y]=c,ord[y]=sz[c]++,ele[c].push_back(y),stk.pop();}while(y!=x);
    	}
    }
    namespace DAG{
    	double f[10100],g[110][110];
    	vector<int>v[10100],u[10100];
    	void Gauss(int x){
    		for(auto i:ele[x]){
    			if(i==T){g[ord[i]][ord[i]]=1,g[ord[i]][sz[x]]=0;continue;}
    			for(auto j:SCC::v[i])if(col[i]==col[j])g[ord[i]][ord[j]]+=1.0/out[i];else g[ord[i]][sz[x]]-=f[j]/out[i];
    			g[ord[i]][sz[x]]-=1;
    			g[ord[i]][ord[i]]+=-1;
    		}
    		for(int i=0;i<sz[x];i++){
    			int mx=i;
    			for(int j=i+1;j<sz[x];j++)if(abs(g[mx][i])<abs(g[j][i]))mx=j;
    			if(i!=mx)for(int j=i;j<=sz[x];j++)swap(g[mx][j],g[i][j]);
    			for(int j=0;j<sz[x];j++){
    				if(i==j)continue;
    				double tmp=g[j][i]/g[i][i];
    				for(int k=i;k<=sz[x];k++)g[j][k]-=tmp*g[i][k];
    			}
    		}
    		for(auto i:ele[x])f[i]=g[ord[i]][sz[x]]/g[ord[i]][ord[i]];
    		for(int i=0;i<sz[x];i++)for(int j=0;j<=sz[x];j++)g[i][j]=0;
    	}
    	queue<int>q;
    	void Topo(){
    		q.push(col[T]);
    		while(!q.empty()){
    			int x=q.front();q.pop();
    			Gauss(x);
    			for(auto y:v[x]){
    				in[y]--;
    				if(!in[y])q.push(y);
    			}
    		}
    	}
    }
    int main(){
    	scanf("%d%d%d%d",&n,&m,&S,&T);
    	for(int i=1,x,y;i<=m;i++)scanf("%d%d",&x,&y),SCC::v[x].push_back(y),SCC::u[y].push_back(x),out[x]++;
    	SCC::dfs1(T),SCC::dfs2(S);
    	for(int i=1;i<=n;i++)if(!SCC::dfn[i])SCC::Tarjan(i);
    	for(int i=1;i<=n;i++)for(auto j:SCC::v[i])if(col[i]!=col[j])DAG::v[col[j]].push_back(col[i]),in[col[i]]++;
    	DAG::Topo();
    	printf("%.3lf\n",DAG::f[S]);
    	return 0;
    }
    

  • 相关阅读:
    每日一练leetcode
    每日一练leetcode
    每日一练leetcode
    springboot搭建过程
    每日一练leetcode
    每日一练leetcode
    每日一练leetcode
    安装 Redis 迎客
    windows系统上面如何后台执行程序 迎客
    jira的详细安装和破解 迎客
  • 原文地址:https://www.cnblogs.com/Troverld/p/14610956.html
Copyright © 2011-2022 走看看