Description
给出一 n×m n × m 的地图, X X 表示该位置被占,否则是空,问任意两个空位置的最短距离的期望值,两个位置是可达的当且仅当两个位置均为空且共边,保证任意两个不会同行同列也不会对角相邻
Input
第一行两个整数 n,m n , m 表示地图行列数,之后输入一 n×m n × m 的地图 (2≤n,m≤1000) ( 2 ≤ n , m ≤ 1000 )
Output
输出任意两个空位置最短距离的期望值,误差不超过 10−6 10 − 6
Sample Input
2 2
..
.
.
.X
.
X
Sample Output
0.888888888889
Solution
首先不考虑 X X 对最短路的影响,那么两个位置和 (x2,y2) ( x 2 , y 2 ) 的最短距离即为 |x2−x1|+|y2−y1| | x 2 − x 1 | + | y 2 − y 1 | ,对于位置 (x,y) ( x , y ) ,其到这 nm n m 个点的最短距离之和为:
∑i=1n∑j=1m(|x−i|+|y−j|)=m∑i=1n|x−i|+n∑j=1m|y−j|=m⋅(C2x+C2n−x+1)+n⋅(C2y+C2m−y+1) ∑ i = 1 n ∑ j = 1 m ( | x − i | + | y − j | ) = m ∑ i = 1 n | x − i | + n ∑ j = 1 m | y − j | = m ⋅ ( C x 2 + C n − x + 1 2 ) + n ⋅ ( C y 2 + C m − y + 1 2 )
根据该公式可以求出任意两点最短距离和 S1 S 1 ,所有 X X 到其他点的最短距离和,任意两个 X X 的最短距离和
注意其中 S2,S3 S 2 , S 3 在考虑 X X 对最短路的影响时也不变,因为任意两个不同行不同列不对角相邻,这样就不会出现连续的(此处连续指的是把共边或对角相邻的点视为相邻)的 X X 把这个和其他点完全隔开,如此求可以求出不考虑 X X 对最短路影响时任意两个空位置的最短距离和
但是注意到 X X 会对两个空位置之间的最短距离造成影响,对于处的 X X ,第行和第 y y 列只有这一个,那么第 x x 行前个空位置到后 m−y m − y 个位置的最短路被 X X 隔离开了,必须绕路才可达,绕路距离至少多,且多 2 2 方案存在,因为一圈都没有其他 X X ,所以只需多走两步绕过即可,这样对每个点对都会多 2 2 步距离,总共就会多步距离,同理考虑第 y y 列会多出步距离
上面只考虑了 X X 对其所在行和所在列的影响,但是注意到如果一些相邻行都有且这些 X X 的列数单调,或者一些相邻列都有且这些 X X 的行数单调,例如第行到第 j j 行都有,且这些 X X 的列数为,那么第 i i 行前个元素到第 k k 行后个元素的所有最短路径均会被这些 X X 阻挡,必须要绕过一个才可达,故最短距离步数增加 2 2 ,总的多出步, i<k≤j i < k ≤ j ,以此累计多出的步数加到答案里即可
现在求出了所有空位置最短距离之和,除以 (nm−num)2 ( n m − n u m ) 2 即为答案,其中 num n u m 为 X X <script type="math/tex" id="MathJax-Element-69">X</script>的个数
Code
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<ctime>
using namespace std;
typedef long long ll;
typedef pair<int,int>P;
const int INF=0x3f3f3f3f,maxn=1005;
int n,m,x[maxn],y[maxn],row[maxn],col[maxn];
char s[maxn][maxn];
int C(int n)
{
return n*(n-1)/2;
}
ll Solve(int x,int y)
{
return (ll)m*(C(x)+C(n-x+1))+(ll)n*(C(y)+C(m-y+1));
}
int main()
{
while(~scanf("%d%d",&n,&m))
{
int res=0;
memset(row,0,sizeof(row));
memset(col,0,sizeof(col));
for(int i=1;i<=n;i++)
{
scanf("%s",s[i]+1);
for(int j=1;j<=m;j++)
if(s[i][j]=='X')
row[i]=j,col[j]=i,x[res]=i,y[res++]=j;
}
ll sum0=0,sum1=0,sum2=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
sum0+=Solve(i,j);
for(int i=0;i<res;i++)
for(int j=0;j<res;j++)
sum1+=abs(x[j]-x[i])+abs(y[j]-y[i]);
for(int i=0;i<res;i++)sum2+=Solve(x[i],y[i]);
sum2=2ll*sum2-sum1;
sum0-=sum2;
for(int i=1;i<=n;i++)
if(row[i])
{
sum0+=4ll*(row[i]-1)*(m-row[i]);
for(int j=i-1;j>=1;j--)
if(!row[j]||row[j]<row[j+1])break;
else sum0+=4ll*(row[i]-1)*(m-row[j]);
for(int j=i+1;j<=n;j++)
if(!row[j]||row[j]<row[j-1])break;
else sum0+=4ll*(row[i]-1)*(m-row[j]);
}
for(int i=1;i<=m;i++)
if(col[i])
{
sum0+=4ll*(col[i]-1)*(n-col[i]);
for(int j=i-1;j>=1;j--)
if(!col[j]||col[j]<col[j+1])break;
else sum0+=4ll*(col[i]-1)*(n-col[j]);
for(int j=i+1;j<=m;j++)
if(!col[j]||col[j]<col[j-1])break;
else sum0+=4ll*(col[i]-1)*(n-col[j]);
}
double temp=1.0*(n*m-res)*(n*m-res);
printf("%.10f\n",1.0*sum0/temp);
}
return 0;
}