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

本文介绍了一种算法,用于计算给定字符矩阵中任意两点间最短耗时的平均值,考虑到障碍物的存在及特定移动规则。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值