Luogu 1514 引水入城 (搜索,动态规划)

Description

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



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

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

Input

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

Output

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

Sample Input

2 5

9 1 5 4 3

8 7 6 1 2

Sample Output

1

1

Http

Luogu:https://www.luogu.org/problem/show?pid=1514

Source

搜索,动态规划

解决思路

首先要找到一个顶部的城市能够最多流向底部的哪些城市。如果有解的话,这个底部的城市一定是一个区间。首先证明这一点

如果顶部蓝色城市只能覆盖黄色的两部分



如果有解的话,那么中间的红色部分由这个绿色城市来覆盖



那既然绿色能够覆盖红色,蓝色为什么就不可以呢?可以发现箭头有交叉。

所以我们发现,如果有解的话,顶部的一个城市一定对应一个底部城市的区间,否则一定无解。

那么第一步就是从每一个顶部城市出发,向下一直走到最后一排寻找出那个区间,这里使用bfs实现。这里需要注意的是,如果每一个顶部城市都进行一遍bfs,势必会超时,所以一个剪枝就是如果这个城市的左边或右边海拔比它高,则这个城市不需要bfs,也就是说我们只对那些比它两边都高的城市bfs。因为海拔高的城市在bfs时一定会覆盖海拔低的相邻城市。

那么接下来,问题就转化为给出若干个区间,选择其中最少的覆盖整个区间。

笔者一开始采用的是排序贪心,然后用差分法标记,贪心地选择,但无奈一直没有调对,只好采用动态规划的方法。

具体是设F[i]表示从左向右覆盖到城市i时所用的最少区间数,那么对于一个区间[l,r]而言,有\(j\in [l,r],F[j]=min(F[j],F[l-1]+1)\)

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std; const int maxN=600;
const int F1[5]={0,1,-1,0,0};//两个F数组分别是在bfs搜索时的四个方向
const int F2[5]={0,0,0,1,-1};
const int inf=2147483647; class Range//区间
{
public:
int l,r;
}; class Pos//位置,bfs里要用
{
public:
int x,y;
}; bool operator <(Range A,Range B)//之前贪心算法时要用到排序
{
return (A.l<B.l)||((A.l==B.l)&&(A.r>B.r));
} int n,m;
int Rangecnt=0;//统计总共找到了多少个可行区间
int Height[maxN][maxN];//海拔
bool vis[maxN][maxN];//bfs中标记是否走过
bool is_cover[maxN];//标记最后一排城市是否走过,方便判断无解
Range R[maxN];//区间
queue<Pos> Q;//bfs队列
int F[maxN];//最后求最少区间覆盖时动态规划 void bfs(int st); int main()
{
memset(is_cover,0,sizeof(is_cover));//初始化
Rangecnt=0;
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)//读入海拔
scanf("%d",&Height[i][j]);
Height[1][0]=0;//为了方便判断,在第一个城市左边和第二个城市右边加入高度为0的城市
Height[1][m+1]=0;
for (int i=1;i<=m;i++)//对于第一行中满足比它两边都高的城市进行bfs搜索区间
if ((Height[1][i]>=Height[1][i-1])&&(Height[1][i]>=Height[1][i+1]))
bfs(i);
int sum=0;//sum用来统计总共覆盖了多少城市,如果sum!=m,则无解
for (int i=1;i<=m;i++)
sum+=(int)(is_cover[i]);
if (sum<m)
{
printf("0\n%d\n",m-sum);
return 0;
}
sort(&R[1],&R[Rangecnt+1]);//其实这个排序是不必要的
memset(F,126,sizeof(F));
F[0]=0;
for (int i=1;i<=Rangecnt;i++)//动态规划求解最少区间覆盖
for (int j=R[i].l;j<=R[i].r;j++)
F[j]=min(F[j],F[R[i].l-1]+1);
printf("1\n%d\n",F[m]);
return 0;
} void bfs(int st)//bfs求解
{
memset(vis,0,sizeof(vis));
Pos init;
init.x=1;
init.y=st;
vis[1][st]=1;
while (!Q.empty())
Q.pop();
Q.push(init);
do
{
Pos u=Q.front();
Q.pop();
for (int i=1;i<=4;i++)
{
int x=u.x+F1[i];
int y=u.y+F2[i];
if ((x<=n)&&(x>=1)&&(y<=m)&&(y>=1)&&(Height[u.x][u.y]>Height[x][y])&&(vis[x][y]==0))
{
Q.push((Pos){x,y});
vis[x][y]=1;
}
}
}
while (!Q.empty());
int l=-1,r=-1;//记录覆盖区间
for (int i=1;i<=m;i++)
{
if ((vis[n][i]==1)&&(l==-1))
l=i;
if ((vis[n][i]==0)&&(l!=-1)&&(r==-1))
r=i-1;
if (vis[n][i]==1)//同时更新is_cover数组
is_cover[i]=1;
}
if (l==-1)//可能出现当前选择的最上面一排的城市无法到达最下面一排,此时直接退出
return;
if (r==-1)//当r==-1说明覆盖到了最后一个城市
r=m;
Rangecnt++;
R[Rangecnt].l=l;
R[Rangecnt].r=r;
return;
}

Luogu 1514 引水入城 (搜索,动态规划)的更多相关文章

  1. [luogu]P1514 引水入城[搜索][记忆化][DP]

    [luogu]P1514 引水入城 引水入城 题目描述在一个遥远的国度,一侧是风景秀美的湖泊,另一侧则是漫无边际的沙漠.该国的行政区划十分特殊,刚好构成一个N 行M 列的矩形 ,如下图所示,其中每个格 ...

  2. 洛谷P1514 引水入城 [搜索,区间DP]

    题目传送门 引水入城 题目描述 在一个遥远的国度,一侧是风景秀美的湖泊,另一侧则是漫无边际的沙漠.该国的行政区划十分特殊,刚好构成一个 N 行×M 列的矩形,如上图所示,其中每个格子都代表一座城市,每 ...

  3. Luogu P1514引水入城【搜索】 By cellur925

    题目传送门 这道题开始看好像并没有什么思路,和搜索好像也并没有什么关系.但是我们手玩下样例就会发现,思路其实就三句话:(写这道题的时候在代码里写的) //我们想知道从第1行的每列往下到干旱区的范围 / ...

  4. Luogu P1514 引水入城

    我承认我有点懒(洛谷已经发过题解了,但我发誓要坚持写博客) 这道题坑了我3天…… 首先一看就与染色问题类似,果断BFS(写DFS炸了) 先将最上面(靠近水)的一行全部扔进队列里,做一遍BFS 再对最下 ...

  5. 【luogu P1514 引水入城】 题解

    题目链接:https://www.luogu.org/problemnew/show/P1514 // luogu-judger-enable-o2 #include <iostream> ...

  6. luogu 1066 引水入城(bfs+贪心)

    90分,有一个点TLE.... 首先可以证明一个东西,如果从上面一排的某个点bfs一次到最下面一排的饮水点不是一个区间的话,那么最后一定所有饮水点不会被覆盖完的. 证明考虑反证法. 所以从上面一排的每 ...

  7. 洛谷 P 1514 引水入城==Codevs 1066

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

  8. [NOIp2010] luogu P1514 引水入城

    跟 zzy, hwx 等人纠结是否回去上蛋疼的董老板的课. 题目描述 如图所示.你有一个 N×MN\times MN×M 的矩阵,水可以从一格流到与它相邻的格子,需要满足起点的海拔严格高于终点海拔.定 ...

  9. LUOGU P1514 引水入城 (bfs)

    传送门 解题思路 拉了很长的战线,换了好几种写法终于过了..首先每个蓄水场一定是对沙漠造成连续一段的贡献,所以可以$bfs$出每种状态,然后做一次最小区间覆盖,但这样的复杂度有点高.就每次只搜那些比左 ...

随机推荐

  1. KVM虚拟机管理——虚拟机克隆

    1. 概述2. 部署基本操作系统虚拟机3. 配置虚拟机3.1 修改/etc/sysconfig/network3.2 删除/etc/sysconfig/network-scripts/ifcfg-et ...

  2. tomcat内存溢出问题记录

    问题说明:公司内网环境中部署的jenkins代码发版平台突然不能访问了,查看tomcat的catalina.out日志发现报错如下: [root@redmine logs]# tail -f /srv ...

  3. Centos7.3下安装Jumpserver 1.0.0(支持windows组件)

    Jumpserver最新版本支持windows组件,废话不多介绍了,下面直接介绍下部署过程: 0)系统环境 CentOS 7.3 IP: 192.168.10.210 [root@jumpserver ...

  4. SQL中not in 和not exists

    在SQL中倒是经常会用到子查询,而说到子查询,一般用的是in而不是exists,先不谈效率问题,就先说说会遇到哪些问题. 用到in当取反的时候,肯定先想到的就是not in.但是在使用not in的时 ...

  5. Wannafly挑战赛25 B.面积并

    链接 [https://www.nowcoder.com/acm/contest/197/B] 分析 特殊优先考虑 首先考虑r>=l这种情况就是圆的面积了 第二就是r<=内切圆的半径,这个 ...

  6. Scrum Meeting 6

                第六次会议 由于之前队员一直在做数据库和编译大作业,课业压力较大,所以软工进度往后拖了好多. No_00:工作情况 No_01:任务说明 待完成 已完成 No_10:燃尽图 N ...

  7. ajax多级菜单栏

    1.jsp 首先ajax查询数据 <script type="text/javascript"> function targetlist() { $.ajax({ ur ...

  8. 手工编程:hello world

    全部用命令行工具和Notepad编辑器,用手工创建并编译一个C的命令行程序:hello world. public class Hello{         public static void ma ...

  9. 业务-----添加Service常用逻辑

    1.参数不能为空 /** * 添加人员时判断是否字段全部传值 * @param request * @return */ private Boolean checkClientByCols(Clien ...

  10. QA

    QA:Quality Assurance,品质保证 IDQA:Individual Document Quality Assurance 设计品质保证 QE:Quantitative Easing 质 ...