问题描述
这天, 小明在玩迷宫游戏。
迷宫为一个 n×n 的网格图, 小明可以在格子中移动, 左上角为 (1,1), 右下角 (n, n)为终点。迷宫中除了可以向上下左右四个方向移动一格以外, 还有 m 个双向传送门可以使用, 传送门可以连接两个任意格子。
假如小明处在格子(x1,y1), 同时有一个传送门连接了格子(x1,y1) 和 (x2,y2), 那么小明既可以花费 1 的步数向上下左右四个方向之一走一格 (不能越过边界), 也可以花费 1 的步数通过传送门走到格子 (x2,y2) 去。
而对于同一个迷宫, 小明每次进入的初始格子是在这n×n 个格子中均匀随 机的 (当然运气好可以直接随机到终点), 他想知道从初始格子走到终点的最短 步数的期望值是多少。
输入格式
输入共 1+m 行, 第一行为两个正整数 n,m 。
后面 mm 行, 每行四个正整数 xi1,yi1,xi2,yi2 表示第 i 个传送门连接的两个格子坐标。
输出格式
输出共一行, 一个浮点数表示答案 (请保留两位小数)。
样例输入
2 1 1 1 2 2
样例输出
0.75
反向搜索 只要搜一次就行
另外本题不标记 因为传送门会使之前的结果不一定是最优的。增加了空间复杂度。
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define fp(i,a,b) for(int i=a;i<=b;++i)
#define PII pair<int,int>
const int N=2e3+10;
const int mod=1e9+7;
const double eps=1e-5;
typedef double db;
int dx[]={0,0,1,-1};
int dy[]={1,-1,0,0};
int n,m;
int dist[N][N];
vector<PII>door[N][N];
bool is_door[N][N];
void bfs()
{
memset(dist,0x3f,sizeof dist);
dist[n][n]=0;
queue<PII>q;
q.push({n,n});
while(q.size())
{
auto t=q.front();
q.pop();
for(int p=0;p<4;p++)
{
int X=dx[p]+t.first,Y=dy[p]+t.second;
if(X<1||X>n||Y<1||Y>n) continue;
if(dist[X][Y]>dist[t.first][t.second]+1)
{
dist[X][Y]=dist[t.first][t.second]+1;
q.push({X,Y});
}
if(is_door[t.first][t.second])//如果当前点可以使用传送门
{
//因为是反向搜图,可以多对一
for(auto s:door[t.first][t.second])
{
//取出里面的点
if(dist[s.first][s.second]>dist[t.first][t.second]+1)
{
dist[s.first][s.second]=dist[t.first][t.second]+1;
q.push({s.first,s.second});
}
}
}
}
}
}
signed main()
{
cin>>n>>m;
for(int i=1;i<=m;i++)
{
int a,b,c,d;
cin>>a>>b>>c>>d;
door[a][b].push_back({c,d});
door[c][d].push_back({a,b});
is_door[a][b]=is_door[c][d]=true;
}
bfs();
int sum=0;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
sum+=dist[i][j];
}
}
cout<<fixed<<setprecision(2)<<1.0*sum/(n*n)<<"\n";
return 0;
}