NOIP 2010 引水入城

题目链接:

点我

题目分析

(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;
}
原文地址:https://www.cnblogs.com/kma093/p/11839550.html