旅行(模拟)

旅行

题目大意

给你一个 n*m 的矩阵,有一些位置不能走,每行每列最多只有一个这样的位置,且它的四个对角线上也不会有不能走的位置。
然后你在所有能走的位置上随机选起点和终点,求从起点移动到终点最短耗时的平均值。
移动:你每个时间可以从一个点移动到它旁边四个点中可以走的其中一个。

思路

首先不难想到求出这个耗时和,再除以选的可能。
然后由于可以选自己,所以选的可能就是空的点数的平方。

接着考虑怎么求耗时和。

首先不难看到这个障碍的分布,不难看出很容易可以用哈密顿的距离走到,那就先统计出来全是哈密顿的耗时和。

然后考虑看那些点不能哈密顿,然后要加多少。
不难想到可以把行跟列分开来。
我们会根据这个障碍的情况,不难看出顶多只会多拐一格,那我们可以行和列找有多少点对要多拐一行 / 列才能走到。(拐过去拐回来就多了两步)

在这里插入图片描述

看这么一个图,黑色的是障碍。
在这里插入图片描述

那对于这一列,一个障碍的上面和下面是互相不能直接用哈密顿到达的,是要拐的。
在这里插入图片描述
而且你还会发现,这个红色的跟上面黄色的也要拐。
那这个要怎么判断呢?
不难看出(推出),是因为它左边(右边也同理)也有障碍,而且它左边的障碍比右边的高,那你往上往左(右)走的过程中,你无法找到一个时机直接跨过障碍到上面,而一定要拐。
那你就枚举红色的位置,然后往两边跑,记录一下就可以了。

列跟行是同一个道理,就不多说了。

代码

#include<cstdio>
#include<cstring>
#define ll long long

using namespace std;

int n, m, a[1001][1001], hang[1001], lie[1001], tot;
int lef[1001], up[1001];
long double an;
char c;

int main() {
	memset(lef, -1, sizeof(lef));
	memset(up, -1, sizeof(up));
	
	scanf("%d %d", &n, &m);
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= m; j++) {
			c = getchar();
			while (c != '.' && c != 'X') c = getchar();
			if (c == '.') a[i][j] = 1, hang[i]++, lie[j]++, tot++;
				else lef[i] = j, up[j] = i;
		}
	
	for (int i = 1; i <= n; i++)//普通的哈密顿距离
		for (int j = 1; j < i; j++)
			an += 2.0 * (i - j) * hang[i] * hang[j];
	for (int i = 1; i <= m; i++)
		for (int j = 1; j < i; j++)
			an += 2.0 * (i - j) * lie[i] * lie[j];
	
	for (int i = 1; i <= n; i++)//找要多走两步的点对
		if (lef[i] != -1) {
			int sum = lef[i] - 1;
			for (int j = i + 1; j <= n && lef[j] != -1 && lef[j] < lef[j - 1]; j++)
				sum += lef[j] - 1;
			for (int j = i - 1; j >= 1 && lef[j] != -1 && lef[j] < lef[j + 1]; j--)
				sum += lef[j] - 1;	
			an += 2.0 * 2.0 * sum * (m - lef[i]);//多走两步,而且起点终点可以翻转,所以乘两个二
		}
	for (int i = 1; i <= m; i++)//列的跟行的一样
		if (up[i] != -1) {
			int sum = up[i] - 1;
			for (int j = i + 1; j <= m && up[j] != -1 && up[j] < up[j - 1]; j++)
				sum += up[j] - 1;
			for (int j = i - 1; j >= 1 && up[j] != -1 && up[j] < up[j + 1]; j--)
				sum += up[j] - 1;	
			an += 2.0 * 2.0 * sum * (n - up[i]);
		}
	
	an /= 1.0 * tot;
	an /= 1.0 * tot;
	printf("%.4Lf", an);
	
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值