引水入城 记忆化搜索
引水入城 记忆化搜索
注意问题本身的特殊性质来解决问题,注意到每个起点到最后一行的路线之间独立,容易想到一种方法:第一行所有点都跑一次dfs,然后标记一下最后一行是谁跑到的,最后问题转换为选取最少个区间覆盖整个区间的问题。
考虑使用记忆化搜索优化。发现每一个起点覆盖最后一行的区间一定是连续的,所以可以用\(l[i][j]\)表示第\(i\)行\(j\)列可以到达最后一行的最左点。此题搜索是其实更多的像是在做dp。注意将最后一行初始化。
#include <cstdio>
#include <cstring>
#include <algorithm>
#define MAXN 505
using namespace std;
bool vis[MAXN][MAXN];
int mv[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
int n,m;
int h[MAXN][MAXN];
int l[MAXN][MAXN];
int r[MAXN][MAXN];
void dfs(int x, int y){
vis[x][y]=1;
for(int i=0;i<4;++i){
int cx=x+mv[i][0],cy=y+mv[i][1];
if(cx<1||cx>n||cy<1||cy>m) continue;
if(h[cx][cy]>=h[x][y]) continue;
if(!vis[cx][cy]) dfs(cx,cy);
l[x][y]=min(l[x][y], l[cx][cy]);
r[x][y]=max(r[x][y], r[cx][cy]);
}
}
void solve(){
for(int i=1;i<=m;++i)
if(!vis[1][i]) dfs(1, i);
int cnt=0;
for(int i=1;i<=m;++i)
if(!vis[n][i]) ++cnt;
if(cnt!=0){
printf("0\n%d", cnt);
return;
}
int cur=1,ans=0;
while(cur<=m){
int mxr=0;
for(int i=1;i<=m;++i)
if(l[1][i]<=cur) mxr=max(mxr, r[1][i]);
++ans;
cur=mxr+1;
}
printf("1\n%d", ans);
}
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]);
memset(vis, 0, sizeof(vis));
memset(l, 0x3f, sizeof(l));
memset(r, 0, sizeof(r));
for (int i=1;i<=m;i++)
l[n][i]=r[n][i]=i;
solve();
}