[BZOJ2702]走迷宫

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"。

Sample Input

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

Sample Output

9.500

HINT

n<=10000,m<=1000000,保证每个强连通分量大小不超过100

题解

本题真的是无法描述啊......

这个高斯消元搞了我一晚上

正解其实想出来了但是就是不会打

既然题目都提示强连通分量了,肯定要跑个tarjan缩点呀

但是,经过一个强连通分量不一定就会INF,因为毕竟一次走不出去还有机会再次走出去

容易发现,INF的条件是“起点终点不连通”或“起点可以到达某个点,该点却不能到达终点”

上述INF情况可以跑正向+逆向dfs来判断

在不INF时,对于每个点,设f[i]为从i点到终点的期望步数,设du[i]为i点出度,f的方程容易想出来:

f[i]=1+sigma(f[j],i与j有边联通)/du[i]

(除了终点T,到了终点之后就不用再走了,因此f[T]=0,终点的出边也可以直接删除)

每个点的f长得和方程一样,那么我们就考虑解方程组:高斯消元

可我们发现数据范围太大(n<=10000)没办法消元

我们发现,题目提示强连通分量大小不超过100,100的范围是可以做的

因此我们考虑按照拓扑序逆序对每一个强连通分量进行高斯消元;

如果按照拓扑序逆序的话,计算某个连通块时,他会涉及到的出点都已经计算完了,所以这样是正确的

代码见下

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<queue>
  4 #include<cmath>
  5 #include<algorithm>
  6 #include<vector>
  7 using namespace std;
  8 const int N=10100;
  9 const int M=1000100;
 10 struct node{int qi,zhong,next;}s[M],z[M],t[M];
 11 bool vis[N],t_vis[N],z_vis[N];
 12 int e,z_e,t_e,adj[N],z_adj[N],t_adj[N];//s最早的图,z缩点后正图,t缩点后反图
 13 int n,m,S,T,id[N],chudu[N];//id为某个点在所属连通块
 14 vector<int>man[N];
 15 int dfn[N],low[N],stack[N],top,num,belong[N],tot;//tarjan相关的数组
 16 double A[210][210],f[N];//A为高斯消元,f为dp数组
 17 inline void add(int qi,int zhong)
 18 {
 19     s[++e].zhong=zhong;s[e].qi=qi;
 20     s[e].next=adj[qi];adj[qi]=e;
 21 }
 22 inline void z_add(int qi,int zhong)
 23 {
 24     z[++z_e].zhong=zhong;z[z_e].qi=qi;
 25     z[z_e].next=z_adj[qi];z_adj[qi]=z_e;
 26 }
 27 inline void t_add(int qi,int zhong)
 28 {
 29     t[++t_e].zhong=zhong;t[t_e].qi=qi;
 30     t[t_e].next=t_adj[qi];t_adj[qi]=t_e;
 31 }
 32 void tarjan(int rt)
 33 {
 34     dfn[rt]=low[rt]=++num;
 35     stack[++top]=rt;vis[rt]=1;
 36     for(int i=adj[rt];i;i=s[i].next)
 37     {
 38         int u=s[i].zhong;
 39         if(!dfn[u])tarjan(u),low[rt]=min(low[u],low[rt]);
 40         else if(!id[u])low[rt]=min(dfn[u],low[rt]);
 41     }
 42     if(dfn[rt]==low[rt])
 43     {
 44         int v,ge=0;tot++;
 45         do
 46         {
 47             v=stack[top--];id[v]=++ge;
 48             belong[v]=tot;man[tot].push_back(v);
 49         }
 50         while(v!=rt);
 51     }
 52 }
 53 void dfs1(int rt)
 54 {
 55     z_vis[rt]=1;
 56     for(int i=z_adj[rt];i;i=z[i].next)
 57         if(!z_vis[z[i].zhong])dfs1(z[i].zhong);
 58 }
 59 void dfs2(int rt)
 60 {
 61     t_vis[rt]=1;
 62     for(int i=t_adj[rt];i;i=t[i].next)
 63         if(!t_vis[t[i].zhong])dfs2(t[i].zhong);
 64 }
 65 inline bool judge()//正反dfs判断INF
 66 {
 67     dfs1(belong[S]);dfs2(belong[T]);
 68     for(int i=1;i<=n;i++)
 69         if(z_vis[i]&&!t_vis[i])
 70             return 0;
 71     return 1;
 72 }
 73 inline void gasse(int b)//高斯消元
 74 {
 75
 76     if(b==belong[T]){f[T]=0;return;}
 77     int size=man[b].size();
 78     memset(A,0,sizeof(A));
 79     for(int i=0;i<size;i++)
 80     {
 81         int p=man[b][i];
 82         A[i][size]=chudu[p];//我统一把出度给乘上去了,没有写分数的形式
 83         for(int j=adj[p];j;j=s[j].next)
 84         {
 85             int u=s[j].zhong;
 86             if(belong[u]==b)//统计连通块自己的系数
 87                 A[i][id[u]-1]--;
 88             else if(u!=T)//统计之前的贡献
 89                 A[i][size]+=f[u];
 90         }
 91         A[i][i]+=chudu[p];
 92     }
 93     for(int i=0;i<size;i++)
 94     {
 95         int p=i;
 96         for(int j=i+1;j<size;j++)
 97             if(fabs(A[p][i])<fabs(A[j][i]))p=j;
 98         if(p!=i)
 99             for(int j=0;j<=size;j++)
100                 swap(A[p][j],A[i][j]);
101         for(int j=i+1;j<size;j++)
102         {
103             double tmp=A[j][i]/A[i][i];
104                 for(int k=i;k<=size;k++)
105                     A[j][k]-=tmp*A[i][k];
106         }
107     }
108     for(int i=size-1;i>=0;i--)
109     {
110         for(int j=i+1;j<size;j++)
111             A[i][size]-=A[j][size]*A[i][j];
112         A[i][size]/=A[i][i];
113     }
114     for(int i=0;i<size;i++)
115         f[man[b][i]]=A[i][size];
116 }
117 void solve(int rt)
118 {
119     for(int i=z_adj[rt];i;i=z[i].next)
120         if(!vis[z[i].zhong])
121             solve(z[i].zhong);
122     vis[rt]=1;
123     gasse(rt);
124 }
125 int main()
126 {
127     scanf("%d%d%d%d",&n,&m,&S,&T);int a,b;
128     if(S==T){printf("0.000");return 0;}
129     for(int i=1;i<=m;i++)
130     {
131         scanf("%d%d",&a,&b);
132         if(a==T)continue;
133         chudu[a]++,add(a,b);
134     }
135     for(int i=1;i<=n;i++)if(!dfn[i])tarjan(i);
136     for(int i=1;i<=n;i++)
137     {
138         for(int j=adj[i];j;j=s[j].next)
139         {
140             int u=s[j].zhong;
141             if(belong[i]!=belong[u])
142             {
143                 z_add(belong[i],belong[u]);
144                 t_add(belong[u],belong[i]);
145             }
146         }
147     }
148     if(judge())
149     {
150         memset(vis,0,sizeof(vis));
151         solve(belong[S]);
152         printf("%.3lf",f[S]);
153     }
154     else{printf("INF");return 0;}
155 } 

BZOJ2707

时间: 2024-10-18 19:48:44

[BZOJ2702]走迷宫的相关文章

NYOJ306 走迷宫(dfs+二分搜索)

题目描述 http://acm.nyist.net/JudgeOnline/problem.php?pid=306 Dr.Kong设计的机器人卡多非常爱玩,它常常偷偷跑出实验室,在某个游乐场玩之不疲.这天卡多又跑出来了,在SJTL游乐场玩个不停,坐完碰碰车,又玩滑滑梯,这时卡多又走入一个迷宫.整个迷宫是用一个N * N的方阵给出,方阵中单元格中填充了一个整数,表示走到这个位置的难度. 这个迷宫可以向上走,向下走,向右走,向左走,但是不能穿越对角线.走迷宫的取胜规则很有意思,看谁能更快地找到一条路

青蛙走迷宫问题(体力值)

题目: 青蛙走迷宫,1代表路通,0代表不通:起点是(0, 0),终点是(0,m - 1);青蛙每次向上走需要消耗体力值为3,向下走不消耗体力值,平走消耗体力值1:根据给定值判断青蛙是否可以根据初始值到达终点,并求出消耗体力值最少的路径: 举例: n = 4, m =4, p = 10(体力值) 4 4 10 1 0 0 1 1 1 0 1 0 1 1 1 0 0 1 1 则结果为:[[0, 0], [1, 0], [1, 1], [2, 1], [2, 2], [2, 3], [1, 3], [

深度优先算法——走迷宫的实现

深度优先搜索算法(Depth-First-Search),是搜索算法的一种.是沿着树的深度遍历树的节点,尽可能深的搜索树的分支.当节点v的所有边都己被探寻过,搜索将回溯到发现节点v的那条边的起始节点.这一过程一直进行到已发现从源节点可达的所有节点为止.如果还存在未被发现的节点,则选择其中一个作为源节点并重复以上过程,整个进程反复进行直到所有节点都被访问为止.属于盲目搜索. 深度优先搜索是图论中的经典算法,利用深度优先搜索算法可以产生目标图的相应拓扑排序表,利用拓扑排序表可以方便的解决很多相关的图

走迷宫

走迷宫 时限:1000ms 内存限制:10000K  总时限:3000ms 描述 判断是否能从迷宫的入口到达出口 输入 先输入两个整数表示迷宫的行数m和列数n,再输入口和出口的坐标,最后分m行输入迷宫,其中1表示墙,0表示空格每个数字之间都有空格. 输出 若能到达,则输出"Yes",否则输出"No",结果占一行. 输入样例 3 30 02 20 0 01 1 00 1 0 输出样例 Yes #include <iostream> using namesp

【老鼠走迷宫二】

/* 老鼠走迷宫二 有问题 */ #include <stdio.h> #include <stdlib.h> void visit(int ,int); int maze[9][9] = { {2, 2, 2, 2, 2, 2, 2, 2, 2}, {2, 0, 0, 0, 0, 0, 0, 0, 2}, {2, 0, 2, 2, 0, 2, 2, 0, 2}, {2, 0, 2, 0, 0, 2, 0, 0, 2}, {2, 0, 2, 0, 2, 0, 2, 0, 2}, {

算法:老鼠走迷宫问题

算法:老鼠走迷宫问题(初) [写在前面] 老鼠走迷宫问题的递归实现,是对递归思想的一种应用. [问题描述] 给定一个二维数组,数组中2表示墙壁,0表示通路,由此数组可展示为一个迷宫图.给定入口位置和出口位置,判断之间是否存在通路并显示出走出迷宫的道路. [代码] 对题目的描述部分 int migo[7][7]={ {2, 2, 2, 2, 2, 2, 2}, {2, 0, 0, 0, 0, 0, 2}, {2, 0, 2, 0, 2, 0, 2}, {2, 0, 0, 0, 0, 2, 2},

数据结构之深度优先搜索(走迷宫)

在此以走迷宫为例: 给定迷宫起点和终点,看能否到达:(xt,yt) void f(int x,int y){ if(x<0||x>21||y<0||y>21){//判断是否超出迷宫 return; } ch[x][y]='#'; for(i=0;i<4;i++){if(ch[x][y]=='.'){ if(x==xt+1&&y==yt+1){ flag=1; return ; } //四个方向 f(x,y+1); f(x,y-1); f(x+1,y); f(x

走迷宫——C++ 算法实现

这两个看了几个不错的走迷宫算法,然后按照自己的方式写了一个实现,代码如下: MyMazeMap.cpp 1 #include <iostream> 2 #include <Windows.h> 3 #include "MazeMap.h" 4 #include "Mazer.h" 5 using namespace std; 6 7 const int SUCCESS = 0; 8 9 int main() 10 { 11 int map[8

第四章 搜索(深度、广度搜索、全排列、走迷宫、再解炸弹人、宝岛探险、水管工游戏)

一.深度优先搜索DFS 深度优先搜索DFS的关键思想是:当下应该怎么做(每个方法都试一遍),这一步解决后,进入下一步,下一步的解决方法和这一步的解决方法是一样的 DFS的基本模型 void dfs(int step) { 判断边界 尝试每一种可能  for(i=1;i<=n;i++) { 继续下一步 dfs(step+1) } 返回 } 1.1全排列 1 //输入一个数n 2 //输出1-n的全排列 3 #include <stdio.h> 4 int n, book[10], a[10