给定一个n行m列的字符矩阵。’.’代表空地,’X’代表障碍。
每秒钟可以往上下左右四个方向其中的一个移动一格(不能往障碍里面撞)。
起点和终点可以在空地里面随机选择(可以重合)。
求从起点移动到终点最短耗时的期望。
每一行、每一列至多有一个障碍。障碍不在对角线方向相邻。 2 ≤ n , m ≤ 1 0 3 2\le n,m\le10^3 2≤n,m≤103。
I n p u t Input Input
第一行两个整数n, m。
接下来n行,每行m个字符’.’或’X’。
O u t p u t Output Output
平均耗时。保留4位小数,四舍五入。
记
E
E
E为空地数,
P
P
P为障碍数
求
∑
M
i
n
D
i
s
u
,
v
E
2
\frac{\sum MinDis_{u,v}}{E^2}
E2∑MinDisu,v
当然不可能枚举
x
1
,
y
1
,
x
2
,
y
2
x_1,y_1,x_2,y_2
x1,y1,x2,y2
解法有可能是枚举
x
,
y
x,y
x,y然后利用之前计算的结果
两个点 u , v u,v u,v之间的最短距离大于它们的曼哈顿距离当且仅当在 x 1 , x 2 x_1,x_2 x1,x2和 y 1 , y 2 y_1,y_2 y1,y2之间有一串墙,它们的 x x x覆盖了 x 1 x_1 x1~ x 2 x_2 x2, y y y覆盖了 y 1 y_1 y1~ y 2 y_2 y2。
会增加多少?
显然当且仅当障碍长成
u· · ·
x· · ·
· · · ·
· x· ·
· · · ·
· · x·
· · · ·
· · · x
· · · v
横过来也行,
x
x
x之间距离大一点也行,
u
u
u和
v
v
v再上一点下一点也没关系,
x
x
x多几列也好
然后用所有空地对的曼哈顿距离加上这些增加的部分就好了。
曼哈顿距离和是多少?
复杂的曼哈顿距离问题一般要分解为
x
x
x方向和
y
y
y方向的距离。
d
i
s
u
,
v
=
Δ
x
+
Δ
y
dis_{u,v}=\Delta x+\Delta y
disu,v=Δx+Δy
枚举行
i
,
j
i,j
i,j。
∑
Δ
y
x
y
=
i
,
x
y
=
j
=
E
y
=
i
∗
E
y
=
j
∗
∣
i
−
j
∣
\sum \Delta y_{x_{y=i},x_{y=j}}=E_{y=i}*E_{y=j}*|i-j|
∑Δyxy=i,xy=j=Ey=i∗Ey=j∗∣i−j∣
枚举列
i
,
j
i,j
i,j。
∑
Δ
x
y
x
=
i
,
y
x
=
j
=
E
x
=
i
∗
E
x
=
j
∗
∣
i
−
j
∣
\sum \Delta x_{y_{x=i},y_{x=j}}=E_{x=i}*E_{x=j}*|i-j|
∑Δxyx=i,yx=j=Ex=i∗Ex=j∗∣i−j∣
增加了多少?
求
x
1
x_1
x1~
x
2
x_2
x2间是否每个
x
i
x_i
xi都有一个障碍并且障碍的
y
y
y单调递增或者递减。
记录一下具体是递增还是递减,障碍的
m
i
n
y
min_y
miny和
m
a
x
y
max_y
maxy。
然后就可以计算纵方向距离多
2
2
2的点对数量了。
y
y
y同理。
考虑到同一行/列不能有两个障碍,所以
x
x
x和
y
y
y的答案不会有重复部分。
具体的实现从障碍来入手会方便得多。
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cctype>
#include<cstring>
#include<ctime>
using namespace std;
char ch=0;
double N,M,K=0;
double EmptyN[1005]={};
double EmptyM[1005]={};
double NPos[1005]={};
double MPos[1005]={};
bool Pt[1005][1005]={};
double Ans=0;
int main()
{
scanf("%lf%lf ",&N,&M);
for(int i=1;i<=N;++i)
{
while(ch!='.'&&ch!='X')ch=getchar();
for(int j=1;j<=M;++j)
{
if(ch=='X')Pt[i][j]=1,NPos[i]=j,MPos[j]=i;
else ++EmptyN[i],++EmptyM[j],++K;
ch=getchar();
}
}
for(int i=1;i<N;++i)
for(int j=i+1;j<=N;++j)
Ans+=2.0f*EmptyN[i]*EmptyN[j]*(j-i);
for(int i=1;i<M;++i)
for(int j=i+1;j<=M;++j)
Ans+=2.0f*EmptyM[i]*EmptyM[j]*(j-i);
for(int i=1;i<=N;++i)
{
if(!NPos[i])continue;
Ans+=4.0f*(NPos[i]-1.0)*(M-NPos[i]);
for(int j=i-1;j&&NPos[j]&&NPos[j]<NPos[j+1];--j)Ans+=4.0f*(NPos[j]-1.0)*(M-NPos[i]);
for(int j=i+1;j<=N&&NPos[j]&&NPos[j]<NPos[j-1];++j)Ans+=4.0f*(NPos[j]-1.0)*(M-NPos[i]);
}
for(int i=1;i<=M;++i)
{
if(!MPos[i])continue;
Ans+=4.0f*(MPos[i]-1.0)*(N-MPos[i]);
for(int j=i-1;j&&MPos[j]&&MPos[j]<MPos[j+1];--j)Ans+=4.0f*(MPos[j]-1.0)*(N-MPos[i]);
for(int j=i+1;j<=M&&MPos[j]&&MPos[j]<MPos[j-1];++j)Ans+=4.0f*(MPos[j]-1.0)*(N-MPos[i]);
}
printf("%.4f",Ans/K/K);
return 0;
}
数据生成器
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<ctime>
#include<cctype>
#include<sstream>
#include<cmath>
#include<cstring>
#include<queue>
using namespace std;
#define ll long long
ll GenRand(const ll Lim1,ll Lim2)
{
++Lim2;
ll ret=Lim1;
int t=0;
while(t<100)
{
if(rand()/(RAND_MAX+1.0)<0.1)break;
ret+=rand();
ret%=Lim2;
++t;
}
while(ret<Lim1)ret+=Lim1;
ret%=Lim2;
return ret;
}
int N,M;
bool Pd[1005]={},Px[1005]={};
stringstream ss;
int main( int argc, char *argv[] )
{
int seed=time(NULL);
if(argc > 1)//如果有参数
{
ss.clear();
ss<<argv[1];
ss>>seed;//把参数转换成整数赋值给seed
}
srand(seed);
N=GenRand(2,1000),M=GenRand(2,1000);
printf("%d %d\n",N,M);
for(int i=1;i<=N;++i)
{
for(int j=1;j<=M;++j)
{
if(rand()/(RAND_MAX+1.0)<0.5)printf(".");
else if(!Pd[i]&&!Px[j])printf("X"),Pd[i]=1,Px[j]=1;
}
printf("\n");
}
return 0;
}
对拍bat↓
@echo off
:loop
data_generator.exe %random% > data.in
std.exe < data.in > std.out
my.exe < data.in > my.out
fc my.out std.out
if not errorlevel 1 goto loop
pause
goto loop