解题思路
拉了很长的战线,换了好几种写法终于过了。。首先每个蓄水场一定是对沙漠造成连续一段的贡献,所以可以$bfs$出每种状态,然后做一次最小区间覆盖,但这样的复杂度有点高。就每次只搜那些比左右高的点。
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<queue> #include<algorithm> using namespace std; const int MAXN = 505; const int inf = 0x3f3f3f3f; inline int rd(){ int x=0,f=1;char ch=getchar(); while(!isdigit(ch)) {f=ch=='-'?0:1;ch=getchar();} while(isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();} return f?x:-x; } int xx[4]={0,0,-1,1},yy[4]={-1,1,0,0}; int n,m,cnt,h[MAXN][MAXN],ans,num,t; int vis[MAXN][MAXN];bool b[MAXN]; queue<int> Q[3]; struct Data{ int l,r; friend bool operator<(const Data a,const Data b){ return a.l==b.l?a.r<b.r:a.l<b.l; } }data[MAXN]; void bfs(int x,int y){ cnt++;Q[1].push(x);Q[2].push(y);data[cnt].l=inf;t++;vis[x][y]=t; while(!Q[1].empty()){ int i=Q[1].front(),j=Q[2].front();Q[1].pop();Q[2].pop(); if(i==n) {data[t].l=min(data[t].l,j);data[t].r=max(data[t].r,j);b[j]=1;} for(register int k=0;k<=3;k++){ int ii=xx[k]+i,jj=yy[k]+j; if(ii<1 || ii>n || jj<1 || jj>m || vis[ii][jj]==t) continue; if(h[i][j]<=h[ii][jj]) continue;vis[ii][jj]=t; // if(ii==n) {data[t].l=min(data[t].l,jj);data[t].r=max(data[t].r,jj);b[jj]=1;} Q[1].push(ii);Q[2].push(jj); } } // if(data[cnt].l==inf) cnt--; // cout<<data[cnt].l<<" "<<data[cnt].r<<endl; } int main(){ n=rd();m=rd(); for(register int i=1;i<=n;i++) for(register int j=1;j<=m;j++) h[i][j]=rd(); for(int i=1;i<=m;i++) if(h[1][i]>=h[1][i-1] && h[1][i]>=h[1][i+1]) bfs(1,i); for(int i=1;i<=m;i++) if(!b[i]) ans++; if(ans) {puts("0");printf("%d\n",ans);return 0;} sort(data+1,data+1+cnt);int now=0;data[cnt+1].l=inf; for(int i=1;i<=cnt;i++) { int mx=0,p=i;if(data[p].r<=now) continue; while(data[p].l-now<=1) {mx=max(mx,data[p].r);p++;} now=mx;num++;i=p-1; } puts("1"),printf("%d",num); return 0; }