水淹七军——一种很新的迷宫问题

题目描述

随着最后通牒的递出,C国的总攻也开始了,由于C国在地形上的优势,C国总司令下令采用水攻,剿灭A国最后的有生力量。
地形图是一个M*N的矩阵,矩阵上每一个点都对应着当前点的高度。C国总司令将选择若干个点进行放水。根据水往低处流的特性,水可以往四个方向的流动,被淹的地方的水面高度便和放水点的高度一样。然而,A国不是一马平川的,所以总会有地方是淹没不到的。你的任务很简单,判断一下A国司令部会不会被淹没掉。
我们将给你完整的地形图,然后给出A国司令部所在位置,给出C国将在哪几个点进行放水操作。你所需要的,就是给出A国司令部会不会被水淹。

关于输入

第一行:一个整数K,代表数据组数。
对于每一组数据:
第1行:符合题目描述的两个整数,M(0 < M <= 200)、N(0 < N <= 200)。
第2行至M+1行:每行N个数,以空格分开,代表这个矩阵上的各点的高度值H(0<=H<=1000)。
第M+2行:两个整数I(0 < I <= M)、J(0 < J <= N),代表司令部所在位置。
第M+3行:一个整数P(0 < P <= M * N),代表放水点个数。
第M+4行至M+P+4行:每行两个整数X(0 < X <= M)、Y(0 < Y <= N),代表放水点。

关于输出

对于每组数据,输出一行,如果被淹则输出Yes,没有则输出No。

分析

首先,面对这么长的题目,完整且正确地理解好题意是一个重点。通过读题,题目的要点大致总结为以下几点:

1、有多个放水点

2、放水点可以淹到上下左右四个点中高度不超过放水点的点(注意高度一样的点也算被淹到)

3、对于地图上的每个点,都有两方面的状态:一是该点的高度,二是该点是否被水淹,因此可以考虑利用struct来表示每个点的状态

接下来的思路就很简单了:先构造一个struct来表示地图上某点的高度和被淹情况,然后从第一个输入的放水点开始(为了节省时间,在开始递归之前可以先判断一下放水点与司令部的高度关系,如果高度低于司令部就直接continue),然后开始递归(类比迷宫),如果达到司令部就返回1。

代码不难给出:

#include<iostream>
#include<string.h>
#include<cmath>
#include<iomanip>
using namespace std;
int k;//数据组数
int m, n;//行、列
int i, j;//司令部位置
int p;//放水点个数
int x[40000], y[40000];//放水点行、列坐标
int a, b, c, d, e, f;//随便多定义几个未知量待用
struct point {//每个点的状态
	int height;//高度
	int condition;//是否被淹,0为没有被淹,1为被淹
};
struct point pt[200][200];
int whether(int a, int b, int h) {//a为当前位置的行坐标,b为当前位置的列坐标,h为放水点高度
	if (a == i - 1 && b == j - 1) {
		return 1;
	}//如果淹到司令部,就返回1,注意:1、pt[200][200]是从0开始计的,因此实际的司令部应该是(i-1,j-i);2、因为只有当放水点高度大于等于司令部高度时才进行递归判断,因此这里不需要再比较高度
	else if (a < 0 || b < 0 || a >= m || b >= n) {
		return 0;
	}//如果当前位置超出地图范围,则返回0
	else if (pt[a][b].height > h) {
		return 0;
	}//如果当前位置高度大于放水点,说明淹不到,则返回0
	else if (pt[a][b].height == h && pt[a][b].condition == 1) {
		return 0;
	}//如果当前位置高度等于放水点且condition==1,说明已经淹过了,不再递归,返回0
	else if (pt[a][b].height < h || pt[a][b].height == h && pt[a][b].condition == 0) {//当当前位置高度小于放水点,或者当前位置高度等于放水点但没被淹过,则需要进行递归
		pt[a][b].condition = 1;//将被淹状况改为“被淹”
		pt[a][b].height = h;//将当前高度改为放水点高度
		return whether(a + 1, b, h) + whether(a - 1, b, h) + whether(a, b + 1, h) + whether(a, b - 1, h);//水继续向四周流动,对当前位置的上下左右进行递归
	}
}
int main() {
	cin >> k;
	for (a = 0; a < k; a++) {
		cin >> m >> n;
		for (b = 0; b < m; b++) {
			for (c = 0; c < n; c++) {
				cin >> pt[b][c].height;
				pt[b][c].condition = 0;
			}
		}
		cin >> i >> j;
		cin >> p;
		for (b = 0; b < p; b++) {
			cin >> x[b] >> y[b];
		}
		d = 0;//用来表示是否能淹到司令部
		for (b = 0; b < p; b++) {
			if (d >= 1) {
				break;
			}
			if (pt[x[b] - 1][y[b] - 1].height < pt[i - 1][j - 1].height) {
				continue;
			}
			else {
				d = whether(x[b] - 1, y[b] - 1, pt[x[b] - 1][y[b] - 1].height);//注意此处放水点的实际行、列坐标均需减去1
			}
		}
		if (d >= 1) {//如果d>=1,说明至少有一个放水点能够淹到司令部
			cout << "Yes" << endl;
		}
		else {
			cout << "No" << endl;
		}
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值