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
分析
我们就先考虑没有X的情况,
那么答案很显然就是所有点两两的曼哈顿距离再除以点对数。
怎样快速计算它们的总和呢?
我们考虑整行整列的计算,
第i行到第j行的
∑|xi−xj|
就等于第i行的空格数量×第j行的空格数量×|i-j|
列的计算方式相同。
现在再来考虑有X的情况,
先观察下面这张图片:
上图中红点到蓝点需要绕行。从直观上来看,一个X下方的点到这个X上方的点需要绕行。如果这个X右边一列(或者左边一列)包含X且本列X之上方,那么到这个X上方的点也需要绕行。根据以上规律,统计出需要+2的点对数加入答案即可AC本题。
code(c++)
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <string.h>
#include <cmath>
#include <math.h>
using namespace std;
int tot,t,k,f[1003],g[1003],n,m,x[1003],y[1003];
double ans,sum;
char ch;
int main()
{
freopen("2.in","r",stdin);
memset(f,0,sizeof(f));
memset(f,0,sizeof(g));
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
ch=getchar();
while((ch!='X')&&(ch!='.'))ch=getchar();
if(ch=='X'){f[i]++;g[1]++;tot++;x[i]=1;y[1]=i;}
for(int j=2;j<=m;j++)
{
ch=getchar();
if(ch=='X'){f[i]++;g[j]++;tot++;x[i]=j;y[j]=i;}
}
}
ans=0.00000000000;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
ans+=(m-f[i])*(m-f[j])*abs(i-j);
for(int i=1;i<=m;i++)
for(int j=1;j<=m;j++)
ans+=(n-g[i])*(n-g[j])*abs(i-j);
for(int i=1;i<=n;i++)
{
if(!x[i])continue;
t=x[i]-1;
for(k=i-1;k&&(x[k])&&(x[k]<x[k+1]);k--)t+=x[k]-1;
for(k=i+1;(k<=n)&&(x[k])&&(x[k]<x[k-1]);k++)t+=x[k]-1;
ans+=t*(m-x[i])*4.0;
}
for(int i=1;i<=m;i++)
{
if(!y[i])continue;
t=y[i]-1;
for(k=i-1;k&&(y[k])&&(y[k]<y[k+1]);k--)t+=y[k]-1;
for(k=i+1;(k<=m)&&(y[k])&&(y[k]<y[k-1]);k++)t+=y[k]-1;
ans+=t*(n-y[i])*4.0;
}
sum=(n*m-tot);
printf("%.4lf\n",ans/sum/sum);
}