引水入城_洛谷1514_搜索 + dp

123 篇文章 0 订阅
66 篇文章 0 订阅

题目描述


在一个遥远的国度,一侧是风景秀美的湖泊,另一侧则是漫无边际的沙漠。该国的行政区划十分特殊,刚好构成一个N 行M 列的矩形,如上图所示,其中每个格子都代表一座城市,每座城市都有一个海拔高度。
这里写图片描述
为了使居民们都尽可能饮用到清澈的湖水,现在要在某些城市建造水利设施。水利设施有两种,分别为蓄水厂和输水站。蓄水厂的功能是利用水泵将湖泊中的水抽取到所在城市的蓄水池中。

因此,只有与湖泊毗邻的第1 行的城市可以建造蓄水厂。而输水站的功能则是通过输水管线利用高度落差,将湖水从高处向低处输送。故一座城市能建造输水站的前提,是存在比它海拔更高且拥有公共边的相邻城市,已经建有水利设施。由于第N 行的城市靠近沙漠,是该国的干旱区,所以要求其中的每座城市都建有水利设施。那么,这个要求能否满足呢?如果能,请计算最少建造几个蓄水厂;如果不能,求干旱区中不可能建有水利设施的城市数目。

输入格式:


输入文件的每行中两个数之间用一个空格隔开。输入的第一行是两个正整数N 和M,表示矩形的规模。接下来N 行,每行M 个正整数,依次代表每座城市的海拔高度。

输出格式:


输出有两行。如果能满足要求,输出的第一行是整数1,第二行是一个整数,代表最少建造几个蓄水厂;如果不能满足要求,输出的第一行是整数0,第二行是一个整数,代表有几座干旱区中的城市不可能建有水利设施。

题解


首先不难想到每一个格子能流到底部的哪些位置是固定的
其次就是对于每个最上方的格子流下来的位置一定是一段连续的区间,记忆化dfs每个格子能流到底部的左和右,就变成求最少线段覆盖区间的问题了
会出现蛇型的数据,第一次用bfs结果TLE,换记忆化就对了。

Code


#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <queue>
#define rep(i, a, b) for (int i = a; i <= b; i++)
#define fill(x, t) memset(x, t, sizeof(x))
#define INF 0x3f3f3f3f
#define N 601
#define M 601
using namespace std;
struct pos{
    int x, y;
    pos operator +(pos b){
        return (pos){x + b.x, y + b.y};
    }
}rec[N][M], dir[4] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
int map[N][M], vis[N][M], f[M];
inline int read(){
    int x = 0, v = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9'){
        if (ch == '-'){
            v = -1;
        }
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9'){
        x = x * 10 + ch - '0';
        ch = getchar();
    }
    return x * v;
}
inline int max(int x, int y){
    return x>y?x:y;
}
inline int min(int x, int y){
    return x<y?x:y;
}
bool cmp(pos x, pos y){
    return x.x < y.x;
}
int dfs(pos now, int n, int m){
    vis[now.x][now.y] = 1;
    if (now.x == n){
        rec[now.x][now.y] = (pos){now.y, now.y};
    }else{
        rec[now.x][now.y] = (pos){INF, -1};
    }
    rep(k, 0, 3){
        pos tar = now + dir[k];
        if (map[tar.x][tar.y] < map[now.x][now.y] && tar.x > 0 && tar.x <= n && tar.y > 0 && tar.y <= m){
            if (!vis[tar.x][tar.y]){
                dfs(tar, n, m);
            }
            rec[now.x][now.y] = (pos){min(rec[tar.x][tar.y].x, rec[now.x][now.y].x), max(rec[tar.x][tar.y].y, rec[now.x][now.y].y)};
        }

    }
}
int main(void){
    int n = read(), m = read();
    rep(i, 1, n){
        rep(j, 1, m){
            map[i][j] = read();
        }
    }
    rep(i, 1, m){
        if (!vis[1][i]){
            dfs((pos){1, i}, n, m);
        }
    }
    sort(rec[1] + 1, rec[1] + m + 1, cmp);
    f[0] = 0;
    rep(i, 1, m){
        f[i] = INF;
        rep(j, 1, m){
            if (rec[1][j].y >= i && rec[1][j].x <= i){
                f[i] = min(f[i], f[rec[1][j].x - 1] + 1);
            }
        }
    }
    int cnt = 0;
    rep(i, 1, m){
        cnt += vis[n][i] ^ 1;
    }
    if (f[m] == INF){
        printf("0");
        f[m] = cnt;
    }else{
        printf("1");
    }
    printf("\n%d\n", f[m]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值