题解 Luogu P1514 【引水入城】

有一种神奇的算法叫做floodfill

就是一个n*m的矩阵,a[i][j]为当前高度,我们可以任选一个点倒水,开始bfs,如果要搜的点没有被搜到过,并且高度小于当前的点,我们就把这个点加入队列中

而这道题我们可以用floodfill做

先假设有解,也就是说假设干旱区里的所有城区都能建水利措施。

对于第一行湖泊,显然我们bfs往下floodfill覆盖的最后一行肯定是个区间,那么对于第一行两个点floodfill后覆盖的最后一行的两个区间,如果左边端点floodfill的右边一部分与右边端点floodfill的左边一部分有相交部分,那么这两个floodfill最后一行所覆盖的区间的左端点肯定是相同的,但是我们选择建蓄水厂肯定是选择右边端点,因为右边端点的floodfill能够多覆盖一部分。

所以我们从左往右枚举最后一行的端点,从最后一行的端点先倒着做一遍floodfill,所覆盖的第一行的区间中选择最右边的端点,再正着做一遍floodfill,直接标记floodfill中扫到的最后一行的区间里的端点,再把这个区间的右端点的右边一个点进行倒着做一遍floodfill......就这样循环做下去直到(n,m)被标记

那么怎么判断无解呢?显然,我们把第一行所有端点都做一遍正着的floodfill,也就是说能建蓄水厂的都建起来。再统计最后一行的没被标记的点数,如果有被标记的,就按照无解的输出形式输出,否则就是有解,按照有解的办法解决问题

可能解释有些不清,可以线下找我)

那么就是我们的代码,其中booms函数是正着floodfill,boom函数是倒着floodfill

Code:

#include<iostream>
#include<cstdio> using namespace std; int dx[]={0,0,1,-1}; int dy[]={1,-1,0,0}; int n,m,a[10001][10001]; bool vis[10001][10001]; bool viss[10001][10001]; int qx[400001],qy[400001]; int ans; int boom(int p) { int tail=1,head=1; vis[n][p]=1; qx[1]=n; qy[1]=p; while(head<=tail) { int x=qx[head],y=qy[head]; head++; for(int i=0;i<4;i++) { int tx=x+dx[i],ty=y+dy[i]; if(tx>=1&&ty>=1&&tx<=n&&ty<=m&&!vis[tx][ty]&&a[x][y]<a[tx][ty]) { qx[++tail]=tx; qy[tail]=ty; vis[tx][ty]=1; } } } for(int i=m;i>=1;i--) if(vis[1][i]) return i; return 0; } void booms(int p) { int tail=1,head=1; qx[1]=1; qy[1]=p; viss[1][p]=1; while(head<=tail) { int x=qx[head],y=qy[head]; head++; for(int i=0;i<4;i++) { int tx=x+dx[i],ty=y+dy[i]; if(tx>=1&&ty>=1&&tx<=n&&ty<=m&&!viss[tx][ty]&&a[x][y]>a[tx][ty]) { qx[++tail]=tx; qy[tail]=ty; viss[tx][ty]=1; } } } } inline int read() { int r=0,w=1; char ch=getchar(); while(ch<'0'||ch>'9'){ if(ch=='-') w=~w+1; ch=getchar(); } while(ch>='0'&&ch<='9'){ r=(r<<3)+(r<<1)+ch-(1<<4)-(1<<5); ch=getchar(); } return r*w; } int main() { //freopen("P1514_5.in","r",stdin); n=read();m=read(); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) a[i][j]=read(); for(int i=1;i<=m;i++) booms(i); ans=0; for(int i=1;i<=m;i++) if(!viss[n][i]) ans++; if(ans){ cout<<0<<endl<<ans; return 0; } for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) vis[i][j]=viss[i][j]=0; int right=1; while(!viss[n][m]&&!vis[n][m]) { int x=boom(right); booms(x); for(int i=m;i>=1;i--) if(!viss[n][i]) right=i; ans++; } cout<<1<<endl<<ans; return 0; }

猜你喜欢

转载自www.cnblogs.com/do-while-true/p/12339640.html