引水入城

[NOIP2010 提高组] 引水入城

如果将每个上面的城市可以覆盖的城市看成可以供给若干个沙漠城市,那么这是一个最小覆盖问题,NP hard

但是,我们注意到如下的性质:

image-20230919201454872

假如一个上城市,不能覆盖中间那两个城市,那么任意一个上城市都不可覆盖它们,因为需要越过这个路线,那么既然走到这条路上,而之前的城市不行,所以现在的城市也不行。

本题可以使用记忆化搜索。

每个点由四周的点转移,而要满足高度限制,所以图是一个拓扑图,可以 DP

转移时只需要同大取大,同小取小即可。

最后先看有没有被覆盖过(无解)。

如果有解,那么每个城市的覆盖都是一个区间,这是一个经典的区间覆盖问题,使用贪心法即可解决。

#include<cstdio>
#include<utility>
#include<algorithm>
#include<cstring>
#define x first
#define y second
using namespace std;
#define Ed for(int i=h[x];~i;i=ne[i])
#define Ls(i,l,r) for(int i=l;i<r;++i)
#define Rs(i,l,r) for(int i=l;i>r;--i)
#define Le(i,l,r) for(int i=l;i<=r;++i)
#define Re(i,l,r) for(int i=l;i>=r;--i)
#define L(i,l) for(int i=0;i<l;++i)
#define E(i,l) for(int i=1;i<=l;++i)
#define W(t) while(t--)
#define Wh while
typedef pair<int,int> pii;
const int N=510,dx[]={-1,1,0,0},dy[]={0,0,-1,1};
int h[N][N],n,m;
pii f[N][N],seg[N];
void dp(int x,int y){
    auto &v=f[x][y];
    if(~v.x)return;
    f[x][y].x=N;
    if(x==n)f[x][y]={y,y};
    L(k, 4){
        int a=x+dx[k],b=y+dy[k];
        if(a>=1&&b>=1&&a<=n&&b<=m&&h[a][b]<h[x][y]){
            dp(a,b);
            f[x][y].x=min(f[x][y].x,f[a][b].x);
            f[x][y].y=max(f[x][y].y,f[a][b].y);
        }
    }
}
int main(){
    #ifndef ONLINE_JUDGE
    freopen("1.in","r",stdin);
    #endif
    scanf("%d%d",&n,&m);
    E(i, n)
        E(j, m)scanf("%d",&h[i][j]);
    memset(f,-1,sizeof f);
    E(j, m)dp(1,j);
    int cnt=0;
    E(i, m)
        if(!~f[n][i].x)++cnt;
    if(cnt){
        printf("0\n%d",cnt);
        return 0;
    }
    E(i, m)seg[i]=f[1][i];//,printf("%d %d\n",seg[i].x,seg[i].y);
    sort(seg+1,seg+1+m);
    int st=1,ans=0;
    for(int i=1;i<=m;){
        int r=-1e9;
        Wh(i<=m&&seg[i].x<=st)r=max(r,seg[i].y),++i;
        ++ans;
        if(r>=m)break;
        st=r+1;
    }
    printf("1\n%d",ans);
    return 0;
}
posted @ 2023-09-19 20:17  wscqwq  阅读(10)  评论(0编辑  收藏  举报