题目链接:
题目分析
\(dfs\)+记忆化
首先\(dfs\)一下求下面的点是不是能全被覆盖到,打一个标记
顺便记忆化一下最后一排左右延伸能延伸到哪里
然后对于最后一排如果能流满,那么还有一个性质,每个起点能覆盖的最后一排是一个连续段
这一点证明可以看洛谷博客,简单来说就是如果两个起点的水流在某个点流到一起了,那顺着分叉一定能流到另外一边的
代码细节实现上还是不够熟练,裸的\(dfs\)写的很多了,但是带一点记忆化/剪枝的有时候就写不出来,还是要多做题
代码:
#include<bits/stdc++.h>
#define N (1000 + 10)
using namespace std;
inline int read() {
int cnt = 0, f = 1; char c = getchar();
while (!isdigit(c)) {if (c == '-') f = -f; c = getchar();}
while (isdigit(c)) {cnt = (cnt << 3) + (cnt << 1) + (c ^ 48); c = getchar();}
return cnt * f;
}
int n, m, Map[N][N], flow[N][N];
int l[N][N], r[N][N], cnt;
const int fx[4] = {-1, 0, 1, 0};
const int fy[4] = {0, 1, 0, -1};
void dfs_(int d, int x, int y){
if (flow[x][y]) return;
flow[x][y] = d;
for (register int i = 0; i < 4; ++i) {
int dx = x + fx[i], dy = y + fy[i];
if (Map[dx][dy] >= Map[x][y]) continue;
if (dx > n || dy > m) continue;
if (dx < 1 || dy < 1) continue;
dfs_(d, dx, dy);
l[x][y] = min(l[dx][dy], l[x][y]);
r[x][y] = max(r[dx][dy], r[x][y]);
}
}
int main() {
// freopen("1.in", "r", stdin);
n = read(), m = read();
memset(l, 0x3f, sizeof l);
for (register int i = 1; i <= n; ++i)
for (register int j = 1; j <= m; ++j) Map[i][j] = read();
for (register int i = 1; i <= m; ++i) l[n][i] = r[n][i] = i;
for (register int i = 1; i <= m; ++i) if (!flow[1][i]) dfs_(i, 1, i);
int flag = 0;
for (register int i = 1; i <= m; ++i)
if (!flow[n][i]) ++flag;
if (flag) {
puts("0");
return printf("%d", flag), 0;
}
int L = 1;
while (L <= m) {
int R = 0;
for (register int i = 1; i <= m; ++i) if (l[1][i] <= L) R = max(R, r[1][i]);
++cnt;
L = R + 1;
}
puts("1"); printf("%d", cnt);
return 0;
}