链接:https://ac.nowcoder.com/acm/contest/7872/M
来源:牛客网
题目描述
众所周知,浙农林是一条河。
由于浙江农林大学的特殊地形,当你在下雨后漫步在农林大路上的时候
难免会出现一脚踩进一个水坑的情况的情况。而农农非常不喜欢踩到水坑的感觉,
请你帮忙设计一个程序来帮助农农判断他能否在不踩入水坑的情况下回到
寝室。
已知,浙江农林大学可以表示为一个 N * N 的矩阵。
对于每个位置有一个海拔数据 h[i][j],当水位高度大于 h[i][j] 的时候,
这个位置就会形成一个水坑。
农农现在的坐标是 (1, 1), 他的宿舍位于 (n, n).
农农只可以沿着上下左右四个方向走。
假如农农现在位于 (2, 2)那么在不考虑水位的情况下,他可以去的地方有
(1, 2),(2,1), (3, 2) ,(2, 3)
输入描述:
N (表示矩阵大小)
接下来 N 行为一个 N * N 的矩阵 h
Q (表示询问数量)
接下来 Q 行每行一个数字,表示当前水位 X
1 <= N <= 1000
1 <= h[i][j] <= 100000
1 <= X <= 100000
1 <= Q <= 100000
输出描述:
共 Q 行,表示在对应水位下,农农能否在不踩入水坑的情况下
回到寝室, 如果农农可以回到寝室,请你输出“Wuhu”, 反之请输出
“Hmmm”
示例1
输入
4
5 2 3 2
4 5 3 4
2 1 4 5
3 3 3 3
2
1
5
输出
Wuhu
Hmmm
说明
对于第一次询问,没有任何一个位置形成水坑,所以农农可以从(1, 1)走到(4, 4)
对于第二次询问 高度小于 5 的位置形成了水坑,其中包括目的地 (4, 4)所以农农无法在
不踩到水坑的情况下走到(4, 4)
比赛的时候因为我做题实在太少了,结果当每给一个水位,我就跑一遍dfs,那肯定是超时了...
后面受到大佬的启发,原来是得先用二分 + dfs 做一遍预处理才行,二分的目的是为了找出最优路,最优路的选择利用了短板效应,也就是最优路里最小的海拔一定大于等于其它别的路里最小的海拔,最优路所能满足的最大水位高度肯定是最高的,找出那个临界水位值
然后小于等于那个临界水位的,都可以从左上角走到右下角;大于那个临界水位就没法从左上角走到右下角了 。
二分模板代码1
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn = 1010;
int h[maxn][maxn];
int vis[maxn][maxn];
int n;
void dfs(int x, int y, int mid) {
if (x < 1 || x > n || y < 1 || y > n || h[x][y] < mid || vis[x][y]) return;
vis[x][y] = 1;
dfs(x - 1, y, mid);
dfs(x + 1, y, mid);
dfs(x, y - 1, mid);
dfs(x, y + 1, mid);
}
int main()
{
cin >> n;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
scanf("%d", &h[i][j]);
int level = 1;
int left = 1, right = 100000; //left一开始为最小的水位高度,right一开始为最大的水位高度
while (left <= right) {
memset(vis, 0, sizeof(vis));
int mid = left + (right - left) / 2;
dfs(1, 1, mid);
if (vis[n][n]) { //看看是否能够抵达右下角
level = mid;
left = mid + 1; //看看还能不能再大
} else {
right = mid - 1; //说明抵达不了右下角,得缩小水位的值
}
}
int q, temp;
scanf("%d", &q);
while (q--) {
scanf("%d", &temp);
if (temp <= level) cout << "Wuhu" << endl;
else cout << "Hmmm" << endl;
}
return 0;
}
二分模板代码2
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn = 1010;
int h[maxn][maxn];
int vis[maxn][maxn];
int n;
void dfs(int x, int y, int mid) {
if (x < 1 || x > n || y < 1 || y > n || h[x][y] < mid || vis[x][y]) return;
vis[x][y] = 1;
dfs(x - 1, y, mid);
dfs(x + 1, y, mid);
dfs(x, y - 1, mid);
dfs(x, y + 1, mid);
}
int main()
{
cin >> n;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
scanf("%d", &h[i][j]);
int level = 1;
int left = 1, right = 100000; //left一开始为最小的水位高度,right一开始为最大的水位高度
while (left < right) {
memset(vis, 0, sizeof(vis));
int mid = left + (right - left) / 2;
dfs(1, 1, mid);
if (vis[n][n]) { //看看是否能够抵达右下角
level = mid;
left = mid + 1; //看看还能不能再大
} else {
right = mid; //说明抵达不了右下角,得缩小水位的值
}
}
int q, temp;
scanf("%d", &q);
while (q--) {
scanf("%d", &temp);
if (temp <= level) cout << "Wuhu" << endl;
else cout << "Hmmm" << endl;
}
return 0;
}