Problem
Description
给定一个n行m列的字符矩阵,’.’代表空地,’X’代表障碍。移动的规则是:每秒钟以上下左右四个方向之一移动一格,不能进入障碍。
计算:在空地中随机选择起点和终点(可以重合,此时最短耗时为0),从起点移动到终点最短耗时的平均值。
每一行每一列至多有1个障碍,并且障碍不在对角线方向相邻。以下矩阵是不合法的:
.X
X.
Input
第一行两个整数n, m。
接下来n行,每行m个字符’.’或’X’。
Output
平均耗时,保留4位小数,四舍五入。
Sample Input
2 2
..
.X
Sample Output
0.8889
Data Constraint
2<=n,m<=1000
Solution
想要拿到前面50分很简单。
先求所有点的曼哈顿距离。首先(x1,y1)到(x2,y2)的曼哈顿距离=|x1−x2|+|y1−y2|
我们枚举i,j两行,那么我们可以发现,随便在i行取一个点,再从j行取一个点,这两个点的横坐标差相同,都是|i−j|。那么Σ|x1−x2|=2∗(第i行空地的数量)∗(第j行空地的数量)∗|i−j|。
用同样的方法求Σ|y1−y2|就可以拿50分。
对于后50分,我们还要观察到某些点对的耗时的性质。
有一些点对他们的耗时=他们的曼哈顿距离+2.因为他们之间有障碍,要绕过障碍才能彼此到达对方。那么这些点满足什么条件呢?
我们先考虑列的情况。
图1
图1中红蓝两个点满足情况。
图2
图2中红蓝两种点和黄绿两种点满足情况。
对于一个有障碍的列,假如我们做到第i列,我们往两边搜,如果发现第j列有障碍,且第j列的障碍在第i列的障碍上方,这个时候我们要将第j列的障碍上面的所有点算上。搜到没有障碍或障碍比第i列的障碍要低就结束。
行的情况自己考虑。
然后我们统计这些点对进答案就行了。
Code
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#define N 1010
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
int n,m,i,j,h[N],l[N],hx[N],lx[N];
double pj,sum,tot,size;
char a[N][N];
int main()
{
memset(hx,127,sizeof(hx));
memset(lx,127,sizeof(lx));
scanf("%d%d\n",&n,&m);
fo(i,1,n)
{
fo(j,1,m)
{
scanf("%c",&a[i][j]);
if (a[i][j]=='.') h[i]++,l[j]++,tot++;
if (a[i][j]=='X') hx[i]=j,lx[j]=i;
}
scanf("\n");
}
fo(i,1,n-1) fo(j,i+1,n) sum+=h[i]*h[j]*abs(i-j)*2/(tot*tot);
fo(i,1,m-1) fo(j,i+1,m) sum+=l[i]*l[j]*abs(i-j)*2/(tot*tot);
fo(i,1,m)
if (lx[i]!=2139062143)
{
size=lx[i]-1;
fd(j,i-1,1) if (lx[j]<lx[j+1]) size+=lx[j]-1;else break;
fo(j,i+1,m) if (lx[j]<lx[j-1]) size+=lx[j]-1;else break;
sum+=size*(n-lx[i])*4/(tot*tot);
}
fo(i,1,n)
if (hx[i]!=2139062143)
{
size=hx[i]-1;
fd(j,i-1,1) if (hx[j]<hx[j+1]) size+=hx[j]-1;else break;
fo(j,i+1,n) if (hx[j]<hx[j-1]) size+=hx[j]-1;else break;
sum+=size*(m-hx[i])*4/(tot*tot);
}
printf("%.4lf",sum);
}