Luogu1514 引水入城

题意比较绕,多看两遍就看懂了

重点在于求满足要求的情况时的答案

可能也是可想的

注意到对于一个第一行的点,如果是满足要求的情况下,
他所能到达的一定是一段连续的区间
考虑如果不连续的话,那一定是绕过了一个区域,
那这一圈都是可以流过去的,而且这一圈都不能流进这个区域
那他就是一个不满足要求的情况

所以对于一个点,他能给供水的是一段区间

那这就转化成了一个线段覆盖问题

正好之前也没做过,这里写一下:

  有两种写法,

  第一种是贪心:在当前已经选出的区间中找一条左端点在其中,
         且右端点最大的线段,之后将这条线段并入已选区间
         直至选完

  第二种是 dp:f[i] 表示到第 i 个位置最少用 f[i] 条线段覆盖
         转移显然是从一段的最小转移,由于要保证最小,
         就用线段树维护区间最小就行了
         转移的位置只要保证能跟之前的拼起来就行

由于比较懒就写了贪心。。。

需要注意的是贪心的话你要保证跟之前区间接起来的话,是已选区间右端点 + 1就行


 代码:

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cctype>
#include <cstdio>
#include <queue>
using namespace std;

const int MAXN = 505, MAXM = 505;

struct POS{
	int x, y;
	POS(int X = 0, int Y = 0) {x = X; y = Y;}
};
struct LINE{
	int l, r;
	LINE(int L = -1, int R = -1) {l = L; r = R;}
	bool operator < (const LINE& b) const {
		return ((l == b.l) ? (r > b.r) : (l < b.l));
	}
	inline void update(LINE val) {
		if (~val.l) {
			l = ((~l) ? min(l, val.l) : val.l);
			r = max(r, val.r);
		}
		return;
	}
}line[MAXN][MAXM], myb[MAXM];
int n, m, totok;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int mp[MAXN][MAXM];
bool vis[MAXN][MAXM];
queue<POS> q;

inline void chk() {
	for (int i = 1; i <= m; ++i) {
		q.push(POS(1, i));
		vis[1][i] = true;
	}
	while (!q.empty()) {
		POS cur = q.front(); q.pop();
		for (int i = 0; i < 4; ++i) {
			int tx = cur.x + dx[i], ty = cur.y + dy[i];
			if (tx < 1 || tx > n || ty < 1 || ty > m || vis[tx][ty] || mp[cur.x][cur.y] <= mp[tx][ty]) continue;
			vis[tx][ty] = true;
			q.push(POS(tx, ty));
		}
	}
	return;
}
LINE dfs(POS cur, POS frm) {
	if (cur.x == n) {
		line[cur.x][cur.y].update(LINE(cur.y, cur.y));
	}
	for (int i = 0; i < 4; ++i) {
		int tx = cur.x + dx[i], ty = cur.y + dy[i];
		if (tx < 1 || tx > n || ty < 1 || ty > m || (tx == frm.x && ty == frm.y) || mp[cur.x][cur.y] <= mp[tx][ty]) continue;
		line[cur.x][cur.y].update(dfs(POS(tx, ty), cur));
	}
	return line[cur.x][cur.y];
}
inline void getans() {

	for (int i = 1; i <= m; ++i) 
		myb[i] = line[1][i];
	sort(myb + 1, myb + m + 1);
	LINE curitv;
	int ans = 1, ptr = 1;
	while (!(~myb[ptr].l)) ++ptr;
	curitv = myb[ptr];
	++ptr;
	while (curitv.r < m) {
		register int curmax = 0;
		while (ptr <= m && myb[ptr].l <= curitv.r + 1) {
			curmax = max(curmax, myb[ptr].r);
			++ptr;
		}
		curitv.r = curmax;
		++ans;
	}
	printf("1\n%d\n", ans);
	return;
}

int main() {
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; ++i) 
		for (int j = 1; j <= m; ++j) 
			scanf("%d", &mp[i][j]);
	chk();
	bool GG = false;
	for (int i = 1; i <= m; ++i) 
		if (!vis[n][i]) {
			GG = true;
			++totok;
		}
	if (GG) {
		printf("0\n%d\n", totok);
		return 0;
	}
	for (int i = 1; i <= m; ++i) 
		dfs(POS(1, i), POS(0, 0));
	getans();
	return 0;
}
posted @ 2018-10-23 21:49  EvalonXing  阅读(111)  评论(0编辑  收藏  举报