【问题描述】
在一个遥远的国度,一侧是风景秀美的湖泊,另一侧则是漫无边际的沙漠。该国的行政 区划十分特殊,刚好构成一个N行M列的矩形,如上图所示,其中每个格子都代表一座城 市,每座城市都有一个海拔高度。
为了使居民们都尽可能饮用到清澈的湖水,现在要在某些城市建造水利设施。水利设施 有两种,分别为蓄水厂和输水站。蓄水厂的功能是利用水泵将湖泊中的水抽取到所在城市的 蓄水池中。因此,只有与湖泊毗邻的第1行的城市可以建造蓄水厂。而输水站的功能则是通 过输水管线利用高度落差,将湖水从高处向低处输送。故一座城市能建造输水站的前提,是 存在比它海拔更高且拥有公共边的相邻城市,已经建有水利设施。
由于第N行的城市靠近沙漠,是该国的干旱区,所以要求其中的每座城市都建有水利 设施。那么,这个要求能否满足呢?如果能,请计算最少建造几个蓄水厂;如果不能,求干 旱区中不可能建有水利设施的城市数目。
【输入格式】
输入的每行中两个数之间用一个空格隔开。 输入的第一行是两个正整数N和M,表示矩形的规模。
接下来N行,每行M个正整数,依次代表每座城市的海拔高度。
【输出格式】
输出有两行。如果能满足要求,输出的第一行是整数1,第二行是一个整数,代表最少 建造几个蓄水厂;如果不能满足要求,输出的第一行是整数0,第二行是一个整数,代表有 几座干旱区中的城市不可能建有水利设施。
【输入样例】
3 6
8 4 5 6 4 4
7 3 4 3 3 3
3 2 2 1 1 2
【输出样例】
1
3
【样例说明】
在上图中用实线框出的三个位置分别建立一座蓄水厂,同时最后一行对应颜色的城市为该蓄水厂所能够接通的城市。不保证存在唯一最优解。
【数据范围】
【原题传送矩阵】
重庆一中题库原题传送矩阵
Vijos题库 原题传送矩阵
Code[VS]题库 原题传送矩阵
【思路梳理】
可能是笔者技术不到家的缘故,多次调试总是第三组数据会超时,最后强行使用手工队列BFS才跌跌撞撞过了此题。
网上大神说是能用最短路径找,,,对此我是一脸懵逼的。还是来点易懂的BFS+贪心的区间归并嘛。
先BFS一遍,看是否存在某个沙漠城市无论从哪一个临海城市出发都不能到达,同时用一个变长数组记录下从每一个临海城市出发可以到达的沙漠城市的id。BFS想必读者都会写,不再赘述(第三组超时的请用手工队列谢谢,STL队列速度堪忧)BFS以后,就可以统计出是情况1(可以向任意一个沙漠城市输水)还是情况0(存在至少1个沙漠城市无论如何都不能得到输水,即vis[i]=0)。如果是情况0,统计vis=0的结点个数输出即可;如果是情况1就需要进行贪心的区间归并。
先给出如下的一个结论:对于情况1,任何一个临海城市能够到达的沙漠城市必然是连续的。如图所示:
将1城所能够覆盖的所有沙漠城市涂为绿色,2城能覆盖的城市涂为橙色,都能覆盖的部分仍然标记为绿色。如果最后一行第八行上的第五个格子不能够被1城覆盖到,那么一定有height[8][5]>height[8][4]和height[8][6]。那么由此一来,想要到达(8,5)就必须从上面的路通过红色格子过来(或者无论如何都要与绿色路径交叉),即使得红色格子(6,5)的高度小于橙色路径(6,5)的上一个点(5,5),且高于绿色路径的上一个点(6,4)的权值。显然对于橙色路径、绿色路径来说,如果经过了同一个城市,那么权值是不会变化的,即绿色路径上的(6,5)权值一定等于橙色路径上(6,5)的权值,形成了矛盾。
所以说就可以贪心地使用区间覆盖来解答此题。
【Cpp代码】
#include<cstdio>
#include<queue>
#include<vector>
#include<iostream>
#include<algorithm>
#include<cstring>
#define maxn 505
using namespace std;
int movex[]={-1,1,0,0};
int movey[]={0,0,-1,1};
int n,m,height[maxn][maxn];
vector<int>num[maxn];
struct data{int x,y;};
struct interval{int left,right;}p[maxn];
void read(int &x)
{
char ch=getchar();x=0;
while(ch<'0' || ch>'9') ch=getchar();
while(ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();}
}
bool inq[maxn][maxn];
data q[maxn*maxn];
int front,rear;
void BFS(data st,int id)
{
memset(inq,false,sizeof(inq));
rear=front=1;
q[rear++]=st;
inq[st.x][st.y]=true;
while(rear!=front)
{
int x=q[front].x,y=q[front++].y;
for(int i=0;i<4;i++)
{
int xx=x+movex[i],yy=y+movey[i];
if(xx<1 || yy<1 || xx>n || yy>m) continue;
if(height[xx][yy]>=height[x][y] || inq[xx][yy]) continue;
q[rear++]=(data){xx,yy};
inq[xx][yy]=true;
}
if(x==n)
{
num[id].push_back(y);
vis[y]=true;
}
}
}
void solve()
{
for(int i=1;i<=m;i++) BFS((data){1,i},i);
bool ok=true;int tot=0;
for(int i=1;i<=m;i++)if(!vis[i]) ok=false,tot++;
if(!ok){printf("0\n%d",tot);return;}
else
{
for(int i=1;i<=m;i++)
{
sort(num[i].begin(),num[i].end());
if(num[i].size()==0) continue;
p[i].left=num[i][0],p[i].right=num[i][num[i].size()-1];
}
int i=1,s=1,ans=0;
while(i<=m && s<=m)
{
int last=-1;
while(p[i].left<=s && i<=m)
{
last=max(last,p[i].right);
i++;
}
ans++;
s=last+1;
}
cout<<"1\n"<<ans;
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++) read(height[i][j]);
solve();
return 0;
}