题意:
一个N*M矩形,每个格子有一个海拔,需要在第一行恰当位置建水利设施将水引到最后一行的每个格子。有两种设施:抽水站,可以建在第一行任意位置;引水站,只要它周围存在一个格子比它地势高且那个格子建的有任意一种水利设施,就可以建造,建造后水引到这里。第一行输出1/0代表能否使得后一行全部引到水。如果是1,求最少需要多少抽水站;如果无法满足,输出最多有多少个无法供水的位置。
题解:
第一行每个点往下BFS,如果能够完全覆盖,那么每个点覆盖的位置一定是连续的一段区间(这一点自己画画图就能推出)。那么原问题转为区间最小覆盖问题,贪心解决。
P.S.下面的剪枝如果不用,第5个点稳TLE:如果第一行当前点被前面第一行的点覆盖到过,那么这次可以不管它(直接处理第一行下一个点),因为它能覆盖的底层区间,之前能走到它的点也一定能覆盖!
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
const int N=502;
int n,m,lf,rg;
int a[N][N];
int dx[4]={0,0,1,-1},dy[4]={1,-1,0,0};
int vis[N][N];
queue<int > qx,qy;
typedef struct Interval {
int st,ed;
Interval(int _st=0,int _ed=0):st(_st),ed(_ed) {}
friend bool operator <(const Interval &a,const Interval &b) {
return a.st==b.st?a.ed>b.ed:a.st<b.st;
}
}Node;
Node p[N];
int tot=0,f=0;
inline int read() {
int x=0;char c=getchar();
while (c<'0'||c>'9') c=getchar();
while (c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-'0',c=getchar();
return x;
}
void bfs(int id) {
while (!qx.empty()) {
int x=qx.front(),y=qy.front();
qx.pop(),qy.pop();
if (n==x) lf=min(lf,y),rg=max(rg,y);
for (int i=0;i<4;++i) {
int nx=x+dx[i],ny=y+dy[i];
if (nx<1||nx>n||ny<1||ny>m) continue;
if (a[x][y]>a[nx][ny]&&vis[nx][ny]^id)
qx.push(nx),qy.push(ny),vis[nx][ny]=id;
}
}
}
inline int MinCover() {
int i=1,cur=0,mx,ret=0;
while (cur<m) {
mx=0;
for (;p[i].st<=cur+1&&i<=tot;++i)
mx=max(mx,p[i].ed);
cur=mx;
++ret;
}
return ret;
}
int main() {
// freopen("P1514.in","r",stdin);
memset(vis,0,sizeof(vis));
n=read(),m=read();
for (int i=1;i<=n;++i)
for (int j=1;j<=m;++j)
a[i][j]=read();
// if (n==500&&a[n][494]==56&&a[n][495]==1) {puts("0");puts("269");return 0;}
for (int i=1;i<=m;++i) {
if (vis[1][i]) continue;
lf=m+1,rg=0;
vis[1][i]=i;
qx.push(1),qy.push(i);
bfs(i);
if (!rg&&lf==m+1) continue;
p[++tot]=Node(lf,rg);
}
for (int i=1;i<=m;++i)
if (!vis[n][i]) ++f;
if (f) {cout<<"0\n"<<f<<endl;return 0;}
sort(p+1,p+tot+1);
// for (int i=1;i<=tot;++i) printf("%d %d\n",p[i].st,p[i].ed);
printf("%d\n%d\n",1,MinCover());
return 0;
}