您的位置:首页 > 其它

jzoj3053. 【NOIP2012模拟10.25】旅行 (Standard IO)

2016-09-05 19:47 645 查看

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

Hint

2<=n,m<=1000

分析

先解决前50%:所有两点间的曼哈顿距离和。

之间的曼哈顿距离等于 。先计算前一部分( 的和。枚举两行i,j。观察到:随便从这两行中各取一个点,这两点的x坐标之差相同。因此,这两行所有点对的 就等于:{第i行空地的数量}{第j行空地的数量}|i-j|*2。

计算 的方法完全相同,略过。两部分答案相加,最后除以空地总数的平方,就得到最终答案。

要解决后50%,关键是要观察到,从A走到B的耗时,要么等于AB的曼哈顿距离,要么等于AB的曼哈顿距离+2。也就是至多绕行一次。这是由题目中的条件“不会有两个X封锁住对角线”保证的。

那么,怎样的点对需要绕行呢,观察:



上图中红点到蓝点需要绕行。从直观上来看,一个X下方的点到这个X上方的点需要绕行。如果这个X右边一列(或者左边一列)包含X且本列X之上方,那么到这个X上方的点也需要绕行。横过来考虑是类似的。

代码

#include<cmath>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<queue>
using namespace std;
int n,m;
double ans,p,b[1005],c[1005],d[1005],e[1005];
char a[1005][1005];
int main()
{
scanf("%d%d\n",&n,&m);
for (int i=1;i<=n;i++) d[i]=2147483647;
for (int i=1;i<=m;i++) e[i]=2147483647;
for (int i=1;i<=n;i++)
{
for (int j=1;j<=m;j++)
{
scanf("%c",&a[i][j]);
if (a[i][j]=='.')
{
p++;
b[i]++;
c[j]++;
}
else
{
d[i]=j;
e[j]=i;
}
}
scanf("\n");
}
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++) ans+=b[i]*b[j]*abs(i-j);
for (int i=1;i<=m;i++)
for (int j=1;j<=m;j++) ans+=c[i]*c[j]*abs(i-j);
for (int i=1;i<=m;i++)
if (e[i]<2147483647)
{
int t=e[i]-1,k=i;
while (k>1 && e[k-1]<e[k])
{
k--;
t+=e[k]-1;
}
k=i;
while (k<m && e[k+1]<e[k])
{
k++;
t+=e[k]-1;
}
ans+=(n-e[i])*t*4;
}
for (int i=1;i<=n;i++)
if (d[i]<2147483647)
{
int t=d[i]-1,k=i;
while (k>1 && d[k-1]<d[k])
{
k--;
t+=d[k]-1;
}
k=i;
while (k<n && d[k+1]<d[k])
{
k++;
t+=d[k]-1;
}
ans+=(m-d[i])*t*4;
}
printf("%.4lf",ans/p/p);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: