引水入城 记忆化搜索

引水入城 记忆化搜索

题面

注意问题本身的特殊性质来解决问题,注意到每个起点到最后一行的路线之间独立,容易想到一种方法:第一行所有点都跑一次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();
}

posted @ 2019-08-23 14:22  Santiego  阅读(146)  评论(0编辑  收藏  举报