题目:洛谷P1514、Vijos P1777、codevs1066。
题目大意:有一个$n×m$的矩阵,每个点都有一个高度,可以在第一行的任意点建立蓄水厂。现在要把水输到最后一行的所有点上,规定水只能流到高度比当前点小的点上。先让你判断能否输到所有点上,如能,输出最少建多少个蓄水厂;如不能,输出最多能输到几个点上。
解题思路:首先把第一行所有点塞进队列里,跑BFS,找出所有能到的点,然后判断能否输到最后一行所有点上。如果不能,输出最后一行能被输到的点的总数。如果能的话,我们依次把第一行每个点能输到的点求出来。
下面证明在能输到最后一行所有点的情况下,第一行每个点能输到最后一行的点一定构成一个连续的区间。
如果出现一个蓄水厂分流到两个不同的区间,那么有下图:
可以发现,红色区域由于已经被蓝色区域包围,所以无论如何都是无法流到的,说明如果有可行的方案,流到的一定是一个连续的区间。
然后就是区间覆盖问题,贪心一下就好了。
C++ Code:
#include<cstdio>
#include<queue>
#include<algorithm>
#include<cstring>
using namespace std;
int n,m,h[505][505],ans;
bool b[505][505];
queue<pair<int,int> >q;
const int dx[]={0,0,1,-1};
const int dy[]={-1,1,0,0};
struct QJ{
int L,R;
bool operator<(const QJ& rhs)const{
if(L!=rhs.L)return L<rhs.L;
return R>rhs.R;
}
}a[505];
void bfs(){
memset(b,1,sizeof b);
for(int i=1;i<=m;++i){
b[1][i]=false;
q.push(make_pair(1,i));
}
while(!q.empty()){
int x=q.front().first,y=q.front().second;
q.pop();
for(int i=0;i<4;++i){
int lx=x+dx[i],ly=y+dy[i];
if(ly>0&&ly<=m&&lx<=n&&b[lx][ly]&&h[x][y]>h[lx][ly]){
b[lx][ly]=false;
q.push(make_pair(lx,ly));
}
}
}
}
void bfs2(int t){
memset(b,1,sizeof b);
b[1][t]=false;
q.push(make_pair(1,t));
int Lft=20000,Rgt=0;
while(!q.empty()){
int x=q.front().first,y=q.front().second;
q.pop();
if(x==n){
if(y<Lft)Lft=y;
if(y>Rgt)Rgt=y;
}
for(int i=0;i<4;++i){
int lx=x+dx[i],ly=y+dy[i];
if(ly>0&&ly<=m&&lx<=n&&b[lx][ly]&&h[x][y]>h[lx][ly]){
b[lx][ly]=false;
q.push(make_pair(lx,ly));
}
}
}
a[t].L=Lft;
a[t].R=Rgt;
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)scanf("%d",&h[i][j]);
bfs();
ans=0;
for(int i=1;i<=m;++i)
if(b[n][i])++ans;
if(ans){
printf("0\n%d\n",ans);
return 0;
}
for(int i=1;i<=m;++i)
bfs2(i);
sort(a+1,a+m+1);
int l=a[1].L,r=a[1].R;
ans=1;
while(r<m){
int p,mx=0;
for(int i=1;i<=m;++i){
if(a[i].L<=l)continue;
if(a[i].L>r+1)break;
if(mx<a[i].R)mx=a[i].R,p=i;
}
l=a[p].L,r=a[p].R;++ans;
}
printf("1\n%d\n",ans);
return 0;
}