P1514 引水入城
先看一下线段覆盖的贪心
【解析】
这道题有两问
(1)能否满足要求
(2)蓄水厂/干旱地有几个
对于第一问,我们可以从第一行的每一个城市出发 dfs 一下它可以走到的所有点,标记,这样的话,如果不能满足要求的话,第 n 行一定会有没有被标记的点
这样就hin好办辣,算一算几个点没有被标记,第二问就OK辣
如果满足要求的话,就hin不好办辣
实际上第一行的每个点最后可以到达第 n 行,水流过的城市是一个连续的区间(详见楼上蟹蟹)
那么就可以用区间覆盖(详见楼上蟹蟹)来做辣
l[x][y]表示点(x,y)可以到达的区间最左端
r[x][y]表示点(x,y)可以到达的区间最右端
【代码】
#include<bits/stdc++.h> using namespace std; int n,m; int high[505][505],l[505][505],r[505][505]; bool vis[505][505]; int dx[4]={1,-1,0,0int dy[4]={0,0,1,-1}; void dfs(int x,int y) { vis[x][y]=true; for(int i=0;i<=3;i++) { int xx=x+dx[i]; int yy=y+dy[i]; if(xx<1||xx>n||yy<1||yy>m) continue ; if(high[xx][yy]>=high[x][y]) continue; if(!vis[xx][yy]) dfs(xx,yy); //见注释 l[x][y]=min(l[x][y],l[xx][yy]); r[x][y]=max(r[x][y],r[xx][yy]); } } int main() { scanf("%d%d",&n,&m); memset(vis,false,sizeof(vis)); memset(l,0x3f,sizeof(l)); memset(r,0,sizeof(r)); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) scanf("%d",&high[i][j]); for(int i=1;i<=m;i++) l[n][i]=r[n][i]=i; for(int i=1;i<=m;i++) if(!vis[1][i]) dfs(1,i); bool flag=false; int cnt=0; for(int i=1;i<=m;i++) if(!vis[n][i]) { flag=true; cnt++; } if(flag) { printf("0 "); printf("%d",cnt); return 0; }
//区间覆盖 int left=1; cnt=0; while(left<=m) { int right=0; for(int i=1;i<=m;i++) if(l[1][i]<=left) right=max(right,r[1][i]); left=right+1; cnt++; } printf("1 "); printf("%d",cnt); return 0; }
【注释】
if(xx<1||xx>n||yy<1||yy>m) continue ; if(high[xx][yy]>=high[x][y]) continue; if(!vis[xx][yy]) dfs(xx,yy);
等价于
bool pan(int x,int y) { return x>=1&&x<=n&&y>=1&&y<=m; } if((pan(xx,yy))&&(high[x][y]>high[xx][yy])) { if(!vis[xx][yy]) dfs(xx,yy); l[x][y]=min(l[x][y],l[xx][yy]); r[x][y]=max(r[x][y],r[xx][yy]); }
由于无论[xx][yy]是否被dfs过,都要用 L[xx][yy] R[xx][yy]来更新L[x][y] R[x][y]
所以要 if 套 if
在此感谢rqy神仙 QVQ喵喵喵
一开始咱也不知道那个错的为啥不行 咱也不问
后来咱问了 咱就会啦