题目描述 Description
在一个遥远的国度,一侧是风景秀美的湖泊,另一侧则是漫无边际的沙漠。该国的行政 区划十分特殊,刚好构成一个N行M列的矩形,如上图所示,其中每个格子都代表一座城 市,每座城市都有一个海拔高度。 为了使居民们都尽可能饮用到清澈的湖水,现在要在某些城市建造水利设施。水利设施 有两种,分别为蓄水厂和输水站。蓄水厂的功能是利用水泵将湖泊中的水抽取到所在城市的 蓄水池中。因此,只有与湖泊毗邻的第1行的城市可以建造蓄水厂。而输水站的功能则是通 过输水管线利用高度落差,将湖水从高处向低处输送。故一座城市能建造输水站的前提,是 存在比它海拔更高且拥有公共边的相邻城市,已经建有水利设施。 由于第N行的城市靠近沙漠,是该国的干旱区,所以要求其中的每座城市都建有水利 设施。那么,这个要求能否满足呢?如果能,请计算最少建造几个蓄水厂;如果不能,求干 旱区中不可能建有水利设施的城市数目。
输入描述 Input Description
输入的每行中两个数之间用一个空格隔开。 输入的第一行是两个正整数N和M,表示矩形的规模。 接下来N行,每行M个正整数,依次代表每座城市的海拔高度。
输出描述 Output Description
输出有两行。如果能满足要求,输出的第一行是整数1,第二行是一个整数,代表最少 建造几个蓄水厂;如果不能满足要求,输出的第一行是整数0,第二行是一个整数,代表有 几座干旱区中的城市不可能建有水利设施。
样例输入 Sample Input
2 5
9 1 5 4 3
8 7 6 1 2
样例输出 Sample Output
1
1
数据范围及提示 Data Size & Hint
【数据范围】 本题共有10个测试数据,每个数据的范围如下表所示: 测试数据编号 能否满足要求 N M 1 不能 ≤ 10 ≤ 10 2 不能 ≤ 100 ≤ 100 3 不能 ≤ 500 ≤ 500 4 能 = 1 ≤ 10 5 能 ≤ 10 ≤ 10 6 能 ≤ 100 ≤ 20 7 能 ≤ 100 ≤ 50 8 能 ≤ 100 ≤ 100 9 能 ≤ 200 ≤ 200 10 能 ≤ 500 ≤ 500 对于所有的10个数据,每座城市的海拔高度都不超过10^6
样例2 说明
数据范围
//分析:题目有两问:能否到达以及相对应的 最少建几个和几个建不了
"毫无疑问,第二个问题是在第一个问题的基础上的,所以,我们首先解决第一问,根据题意,
能建成的条件是海拔高的到海拔低的,而且蓄水站只能建在第一行,所以只需要考虑从第一行建站向外扩展,
能否扩展到最后一行,可以用bfs(dfs好像也可以,but没做过),对第一行的每一个点进行bfs,用vis记录可到达的点,
最后for一遍看一下最后一行是否全都被访问过即可,如果有没有被访问过的点,那就num++,最后输出num即可"
如果全部访问过,那么就要求最少要在第一行建几个蓄水站 因为我们上一步做的是bfs,而且是一个点一个点的进行bfs,
所以我们很容易知道第一行任意一个点所能到达的最后一行的点是谁,有几个,这样就可以求出第一行每一个点
在最后一行的覆盖范围,而且这个范围是一段连续的区间/因为从海拔高处向海拔低处传递,所以传递的过程是在
走一个递减的路线,所以能走就一定递减而且只能走相邻的海拔比自己低的点所以连续/那么现在我们就得到了一些区间,
既然让我们求最少建几个,那么这些覆盖范围就一定会有重复,所以才会有一些点根本不需要建蓄水站,所以才能求最小,
所以我们要对这些区间/*做线段覆盖*/,贪心和DP都可以,不过,不会用DP(话说codevs线段覆盖系列只做了1),
那就用贪心:我们对区间按照l从小到大排序,l一样的按r从小到大排序,每次取能与上一条线段接上而且r最大的
(*这样可以为剩下的部分留出更少的能选的线段,那就必然会最少*不用担心剩下的部分没有足够的线段覆盖,出现断层,
∵只要没有覆盖满,就一定还会有能覆盖后面剩下的部分的线段,毕竟我们考虑的是能全部覆盖的情况,这一点是一定能成立的)
#include<iostream>
#include<cstdio>
#include<queue>
#include<algorithm>
using namespace std;
int n,m,h[501][501],ans=0,lalala=-1,ok=0;
bool vis[501][501];
int xx[4]={1,0,-1,0},yy[4]={0,1,0,-1};
queue <int> qx;
queue <int> qy;
struct node{
int l,r;
}e[501];
bool cmp(node c,node d) { return c.l == d.l ? c.r < d.r : c.l<d.l; }
void bfs(int i0,int j0)
{
if(ok==1)//记录每一个点的覆盖范围时,要先初始化,因为可以有重叠的区间
{
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
vis[i][j]=false;
}
e[j0].l=100000; e[j0].r=-1;
if(n==1) { e[j0].l=j0; e[j0].r=j0; }//只有一行时,要单独初始化,初始化方式不一样
qx.push(i0); qy.push(j0); vis[i0][j0]=true;
while(!qx.empty()&&!qy.empty())
{
int x=qx.front(); qx.pop(); int y=qy.front(); qy.pop();
for(int i=0;i<=3;i++)
for(int j=0;j<=3;j++)
{
int nx=x+xx[i]; int ny=y+yy[i];
if(nx <= n&&nx>=1&&ny <= m&&ny>=1&&h[nx][ny]<h[x][y])
{
if(nx==n&&ok==1)
{
e[j0].l=min(e[j0].l,ny);
e[j0].r=max(e[j0].r,ny);
}
if(!vis[nx][ny]) { qx.push(nx); qy.push(ny); vis[nx][ny]=true; }
}
}
}
return;
}
inline int read()
{
int a=0,f=1; char c=getchar();
while (c<'0'||c>'9') {if (c=='-') f=-1; c=getchar();}
while (c>='0'&&c<='9') {a=a*10+c-'0'; c=getchar();}
return a*f;
}
int main()
{
int tot=0,k,s=0,line=1;
n=read(); m=read(); k=1;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
vis[i][j]=false;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
h[i][j]=read();
//不满足要求
for(int i=1;i<=m;i++) bfs(1,i);
for(int i=1;i<=m;i++) { if(!vis[n][i]) tot++; }
if(tot) { printf("0\n%d\n",tot); return 0; }
//满足要求
for(int i=1;i<=m;i++) ok=1,bfs(1,i);
int now=1,list=0;
sort(e+1,e+m+1,cmp);
//for(int i=1;i<=m;i++) printf("%d %d\n",e[i].l,e[i].r);
int minn=100000;
for (int i=1;i<=m;i++)//用for有bug,不能处理开头的(排序后在第一位的)覆盖区间为[1,1]的线段
{//所以从2开始直接忽略掉[1,1],反正最后都要加上这一条,只需要用minn来标注一下这种特殊的数据 (见下文)
//最后+1即可
if (now+1>=e[i].l) list=max(list,e[i].r);
else now=list,list=max(list,e[i].r),ans++,minn=min(minn,e[i-1].l);
if(now==m) break;
}
if (now!=m||minn!=1) ans++;//now!=m不写也可以,现在想想没什么必要
printf("1\n%d\n",ans);
return 0;
}
//if(now==m)break;防止第一行中的不能到达最后一行的点被计入(因为初始化为100000,一定>now)
//now+1>=e[i].l--->能与上一条线段接上的条件
以下内容,有错误请指出哦Thanks♪(・ω・)ノ
设置minn变量的原因:用while做区间覆盖,tle,看到了这种方法,做完后自造数据测代码,
但是被自己卡住了,才发现now初始化为1而自己是以0来做的,这才发现用for循环的bug,
它无法处理开头为[1,1]区间的数据,从1开始的话不是最优的,所以从2开始直接忽略这个区间,
反正最后都得加上是吧
附:让我找到bug的数据(可以手推一下)
输入:
2 5
2 1 4 1 9
1 1 3 4 7
输出:
1
2
//初始化要初始好,一次初始完成,初始化为0但从1开始,随手+1再用可能会出bug(如上)
/*
//现实教会我成长,//体会到了
我只想说:终于把你"AC"了......✿✿ヽ(°▽°)ノ✿
*/