题目:洛谷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 %d ",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 %d ",ans); return 0; }