[NOIP2012模拟10.25] 旅行 [构造]

给定一个n行m列的字符矩阵。’.’代表空地,’X’代表障碍。
每秒钟可以往上下左右四个方向其中的一个移动一格(不能往障碍里面撞)。
起点和终点可以在空地里面随机选择(可以重合)。
求从起点移动到终点最短耗时的期望。
每一行、每一列至多有一个障碍。障碍不在对角线方向相邻。 2 n , m 1 0 3 2\le n,m\le10^3
I n p u t Input
第一行两个整数n, m。
接下来n行,每行m个字符’.’或’X’。
O u t p u t Output
平均耗时。保留4位小数,四舍五入。

E E 为空地数, P P 为障碍数
M i n D i s u , v E 2 \frac{\sum MinDis_{u,v}}{E^2}

当然不可能枚举 x 1 , y 1 , x 2 , y 2 x_1,y_1,x_2,y_2
解法有可能是枚举 x , y x,y 然后利用之前计算的结果

两个点 u , v u,v 之间的最短距离大于它们的曼哈顿距离当且仅当在 x 1 , x 2 x_1,x_2 y 1 , y 2 y_1,y_2 之间有一串墙,它们的 x x 覆盖了 x 1 x_1 ~ x 2 x_2 y y 覆盖了 y 1 y_1 ~ y 2 y_2

会增加多少?
显然当且仅当障碍长成
u· · ·
x· · ·
· · · ·
· x· ·
· · · ·
· · x·
· · · ·
· · · x
· · · v
横过来也行, x x 之间距离大一点也行, u u v v 再上一点下一点也没关系, x x 多几列也好

然后用所有空地对的曼哈顿距离加上这些增加的部分就好了。
曼哈顿距离和是多少?
复杂的曼哈顿距离问题一般要分解为 x x 方向和 y y 方向的距离。
d i s u , v = Δ x + Δ y dis_{u,v}=\Delta x+\Delta y
枚举行 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|
枚举列 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|

增加了多少?
x 1 x_1 ~ x 2 x_2 间是否每个 x i x_i 都有一个障碍并且障碍的 y y 单调递增或者递减。
记录一下具体是递增还是递减,障碍的 m i n y min_y m a x y max_y
然后就可以计算纵方向距离多 2 2 的点对数量了。
y y 同理。
考虑到同一行/列不能有两个障碍,所以 x x 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;
}

对拍参照的std

数据生成器
#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

猜你喜欢

转载自blog.csdn.net/Estia_/article/details/83019790