[题解] luogu P1514 引水入城 DFS + 贪心覆盖区间

原题链
打了一上午,我真是弱爆了

看完题目,可以很显然的想到一种搜法:
DFS/BFS第1个到第m个临湖城市,求出干旱区城市能否全有水,很显然,这样时间会炸

  • 此时,我们可以在选择搜第i个临海城市加一个剪枝:
if (c[1][i-1] < c[1][i] && c[1][i+1] < c[1][i])
	dfs();

意思说,当且仅当 i 的前一个临湖城市和后一个临海城市的海拔都小于当前临湖城市海拔,我才去搜这个城市
为什么?
你会发现,一个临湖城市要是比自己相邻的临湖城市海拔小,就可以通过相邻的城市个自己输水,那么自己再建个蓄水站就没有必要了

当我们完成了搜索,就已经做完了第一种情况,也就是干旱区城市不能全建输水站的情况,记录下输出即可

此时,我们要求的是最少建几个蓄水站,那么可以贪心:

  • 可以发现,现在既然每个干旱区城市都可以建到输水站,那么第 i 个临海城市所可以扩展出来的干旱城市是一段连续的区间

  • 我们这样想:在前面dfs的时候,记录第 i 个临湖城市可以扩展出来的干旱区城市区间的左端点和右端点,这样问题就转换成了:给定一个长度为m的区间,再给出n条线段的起点和终点,求最少使用多少条线段可以将整个区间完全覆盖

  • 这就是贪心区间覆盖,我们可以通过以下步骤求解,好好理解(巨佬自行屏蔽):

  1. 将所有线段从小到大排序,先比较左端点,一样的话就比较右端点
  2. 每次在线段中找出所有左端点小于等于当前已覆盖到的区间右端点的线段,选择右端点最大并且大于当前已覆盖到的区间右端点,重复以上操作直至覆盖整个区间

其正确性就不再证明

然后这道题就做完了:

#include <bits/stdc++.h>
using namespace std;

int n, m, c[550][550], _desert[550];
int L, R, now, ans, maxr, maxnum, vis[550][550];
int move1[5] = {0, 1, 0, -1, 0};
int move2[5] = {0, 0, 1, 0, -1};

struct Range {
    int l, r;
}_sea[550];
int vcmp(Range a, Range b) {
    if (a.l < b.l) return a.l > b.l;
    if (a.l > b.l) return a.l < b.l;
    return a.r < b.r;
}
inline void GREEDY() {
    sort(_sea + 1, _sea + m + 1, vcmp);
    for (int i = 1; i <= m; ++i)
        if (_sea[i].l == 1) 
			R = max(R, _sea[i].r);
    maxr = R;
    ++ans;
    while (R < m) {
    	maxr = 0;
        for (int i = 1; i <= m; ++i)
            if (_sea[i].l <= R + 1 && _sea[i].r > maxr)
                maxr = _sea[i].r;
        R = maxr;
        ++ans;
    }
    printf ("1
%d
", ans);
}
void dfs_answer(int x, int y) {
	vis[x][y] = 1;
    if (x == n) {
        _desert[y] = 1;
        _sea[now].l = min(_sea[now].l, y);
        _sea[now].r = max(_sea[now].r, y);
    } int xi, yi;
    for (int i = 1; i <= 4; ++i) {
        xi = x + move1[i]; 
        yi = y + move2[i];
        if (xi > 0 && xi <= n && yi > 0 && yi <= m)
            if (c[xi][yi] < c[x][y] && vis[xi][yi] == 0)
                dfs_answer(xi, yi);
    }
}
int main() {
    scanf ("%d%d", &n, &m);
    for (int i = 1; i <= n; ++i)
        for (int j = 1; j <= m; ++j)
            scanf ("%d", &c[i][j]);
    for (int i = 1; i <= m; ++i) {
        _sea[i].l = 
    	_sea[i].l = m + 1;
        _sea[i].r = 0;
    	if (c[1][i - 1] > c[1][i] || c[1][i + 1] > c[1][i])
    		continue;
        now = i;
        dfs_answer(1, i);
        memset(vis, 0, sizeof(vis));
    }
    for (int i = 1; i <= m; ++i)
        if (_desert[i] == 0) ++ans;
    if (ans != 0) {
        printf ("0
%d
", ans);
        return 0;
    }
    GREEDY();
    return 0;
}
原文地址:https://www.cnblogs.com/martixx/p/11232431.html