方法:二分答案+dfs
二分一个mid,此次刺杀的最大伤害,作为判断条件来dfs,二分,更新。
我们二分一个答案mid来表示一个界限,如果当前这个格子的伤害代价比mid小则可以走否则就不走,每次check函数只需判断能否从第一行走到最后一行即可,因为每一行的每个门都是相连的,所以只要有一个能到,那么我们再派m-1个人顺着这条路过去再沿着横向的门过去就好啦,因为第一行和最后一行的伤害值为零,所以这么做莫得问题。
问:
为什么dfs时只要判断是否能到达即可,我们不是要找他的最大值来表示这一次的伤害值嘛?
答:
因为我们二分的这个值,最后二分出来的一定是某个点产生的伤害值,也就是我们最后的答案(是最大值嘛,判断此点是否可行就是判断他是否是比mid小,所以mid就是此次的最大值就是答案)这也解释了为什么我们二分的是伤害值最后却可以输出二分的边界的问题。
//每次check一个值dfs
//只要map[i][j]的值比mid小就能走
//否则就不能走//二分答案
//每次check的时候记录一个flag
//flag每次清零
//如果在当前的check下能够到达最后一行
//即令flag表示为真
//如果flag为真说明可以到达
//继续二分答案
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int N = 1010;
int n, m, map[N][N], l, r, mid, x, y;
int dx[4] = {-1, 1, 0, 0}, dy[4] = {0, 0, -1, 1};
bool flag = 0, vis[N][N];
int read() {
int s = 0, w = 1;
char ch = getchar();
while(!isdigit(ch)) {if(ch == '-') w = -1;ch = getchar();}
while(isdigit(ch)) {s = s * 10 + ch - '0';ch = getchar();}
return s * w;
}
void dfs(int xx, int yy) {
if(xx == n) {flag = 1;return;}
for(int i = 0; i < 4; i++) {
x = xx + dx[i], y = yy + dy[i];
if(x >= 1 && x <= n && y >= 1 && y <= m && map[x][y] <= mid && !vis[x][y]) {
vis[x][y] = 1;
dfs(x, y);
vis[x][y] = 0;
if(flag) break;
}
}
}
bool check(int x) {
flag = 0;
memset(vis, 0, sizeof(vis));
dfs(1, 1);
if(flag) return 1;
return 0;
}
int main() {
n = read(), m = read();
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
map[i][j] = read(), r = max(r, map[i][j]);
while(l + 1 < r) {
mid = (l + r) >> 1;
if(check(mid)) r = mid;
else l = mid;
}
printf("%d\n", r);
return 0;
}
谢谢收看, 祝身体健康!