<饮水入城> (神坑爆搜)

<饮水入城> (神坑爆搜)

题目描述

在一个遥远的国度,一侧是风景秀美的湖泊,另一侧则是漫无边际的沙漠。该国的行政区划十分特殊,刚好构成一个N 行M 列的矩形,如上图所示,其中每个格子都代表一座城市,每座城市都有一个海拔高度。

这里写图片描述

为了使居民们都尽可能饮用到清澈的湖水,现在要在某些城市建造水利设施。水利设施有两种,分别为蓄水厂和输水站。蓄水厂的功能是利用水泵将湖泊中的水抽取到所在城市的蓄水池中。

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

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

输出格式:

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

输入输出样例
输入样例#1:

【输入样例1】
2 5
9 1 5 4 3
8 7 6 1 2

【输入样例2】
3 6
8 4 5 6 4 4
7 3 4 3 3 3
3 2 2 1 1 2

输出样例#1:

【输出样例1】
1
1

【输出样例2】
1
3

说明
这里写图片描述

【样例1 说明】

只需要在海拔为9 的那座城市中建造蓄水厂,即可满足要求。

【样例2 说明】

上图中,在3 个粗线框出的城市中建造蓄水厂,可以满足要求。以这3 个蓄水厂为源头

在干旱区中建造的输水站分别用3 种颜色标出。当然,建造方法可能不唯一。

数据范围
这里写图片描述

  • 先平复一下激动的心情,毕竟这题本蒟篛wa了40多天。。。
  • 这个题其实就是洪水漫延的升级版,所以不算难,比较难写而已;
  • 当然也有难点
    1. 思路:若最后一行的点全会被水覆盖,则从第一行的某个点引出的水所能覆盖的最后一行的区间一定是连续的。比如不可能覆盖最后一行的[2,5],[7,9]两个断开的区间。
    证明:(反证) 如果区间断开,则意味着能连向中间断开部分的某个点的点都无法将水运送给它(高度低),所以第一行其它点引出的水也无法到达这个点,这个点就永远不会有水;
    2. 搜索既可以写dfs,也可以写bfs。但是两种写法都有很多坑;
    搜索的目的有两个,判断最后一行是否都能覆盖,以及从第一行的某个点引出的水所能覆盖的最后一行的区间;
    3. 最后有一个区间覆盖问题:利用之前搜索得出的第一行每个点所能覆盖的区间,将问题转化为n段区间覆盖n个点所需的最小区间数,运用DP或贪心解决;
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#define For(i,n) for(int i=1;i<=n;++i)
using namespace std;
int n,m;    //N 行,每行M 个正整数
int h[510][510];
bool e[510][510],dry[510];
int lft[510],rgt[510];//初始化
int sum;

void init();
void bfs(int,int);
void DP();

int main(){
    init();
    if(n==1){
        printf("%d\n",1);
        For(i,m) 
         if(h[1][i]>=h[1][i-1]&&h[1][i]>=h[1][i+1])
          sum++;
        printf("%d",sum);
        return 0;
    }
    memset(lft,0x3f3f3f3f,sizeof(lft));
    For(i,m)
     if(h[1][i]>=h[1][i-1]&&h[1][i]>=h[1][i+1]){//剪枝(只有左右端点的高度都比这个点的高度小,才需要搜)
        //cout<<i<<"   ";
         bfs(1,i);
     }

    For(i,m) if(!dry[i]) sum++;
    if(sum) printf("%d\n%d",0,sum);
    else DP();
}

void init(){
    scanf("%d%d",&n,&m);
    For(i,n) For(j,m) scanf("%d",&h[i][j]);
}

queue<int>tx,ty;
int mx[5]={0,1,0,-1,0};
int my[5]={0,0,1,0,-1};
int x,y;
int nx,ny;
void bfs(int a,int b){
    memset(e,0,sizeof(e));
    tx.push(a);ty.push(b);
    e[a][b]=1;                            //cout<<b;
    while(!tx.empty()&&!ty.empty()){
        nx=tx.front();
        ny=ty.front();
        For(i,4){
            x=nx+mx[i];
            y=ny+my[i];
            if(x>=1 && x<=n && y>=1 && y<=m && e[x][y]==0 && h[x][y]<h[nx][ny]){
                tx.push(x);ty.push(y);
                e[x][y]=1;
                if(x==n){//记录区间
                    dry[y]=1;//cout<<y<<endl;
                    lft[b]=min(lft[b],y);
                    rgt[b]=max(rgt[b],y);
                }
            }

        }
        tx.pop();ty.pop();
    }
}

int g[510];
void DP(){

    memset(g,0x3f3f3f3f,sizeof(g));
    g[0]=0;
    For(i,m)
     For(j,m)
      if(lft[j]<=i && rgt[j]>=i)
       g[i]=min(g[i],g[lft[j]-1]+1);//    For(i,m) cout<<lft[i]<<"  "<<rgt[i]<<endl;
    printf("%d\n%d",1,g[m]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值