题解 引水入城

10 篇文章 0 订阅
7 篇文章 0 订阅

@luogu
@LOJ

从上向下记忆化搜索,保存每个点能覆盖的最底层的区间。如果一个点能到达的区间不连续,那么一定没有方案。然后做一个基础贪心:从n个区间中选出尽量少的区间,覆盖1-n

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 510
#define ll long long
#define max(x,y) ((x)>(y) ? (x) : (y))
#define min(x,y) ((x)<(y) ? (x) : (y))
using namespace std;

inline int getint() {
    int t=0,p;
    char ch=getchar();
    for(;ch!='-' && !(ch>='0' && ch<='9');ch=getchar());
    if(ch=='-') p=-1,ch=getchar();
    else p=1;
    for(;ch>='0' && ch<='9';ch=getchar()) {
        t=t*10+ch-48;
    }
    return t*p;
}

struct Line {
    int l,r;
    Line() {
        l=r=0;
    }
    bool operator < (const Line &o) const {
        return l<o.l;
    }
} b[N][N],c[N];

const int dx[4]={0,1,0,-1};
const int dy[4]={-1,0,1,0};
int a[N][N],n,m;
bool flag,f[N][N];

void Iscream(int x,int y) {   //记忆化搜索,b[i][j] 保存每个点能覆盖的底层区间。
    if(f[x][y]) return;
    f[x][y]=true;
    if(x==n) {b[x][y].l=y;b[x][y].r=y;}
    for(int i=0;i<4;i++) {
        int nx=x+dx[i];
        int ny=y+dy[i];
        if(nx>0 && nx<=n && ny>0 && ny<=m && a[nx][ny]<a[x][y]) {
            if(!f[nx][ny]) Iscream(nx,ny);
            if(b[nx][ny].l==0 && b[nx][ny].r==0) continue;
            if(b[x][y].l==0 || b[x][y].r==0) {
                b[x][y].l=b[nx][ny].l;
                b[x][y].r=b[nx][ny].r;
            } else {
                if(b[x][y].r<b[nx][ny].l-1 || b[x][y].l>b[nx][ny].r+1) {flag=true;}
                b[x][y].l=min(b[x][y].l,b[nx][ny].l);
                b[x][y].r=max(b[x][y].r,b[nx][ny].r);
            }
        }
    }
}

int main() {
//	freopen("flow.in","r",stdin);
//	freopen("flow.out","w",stdout);
    n=getint();m=getint();
    for(int i=1;i<=n;i++) {
        for(int j=1;j<=m;j++) {
            a[i][j]=getint();
        }
    }
    flag=false;
    memset(f,false,sizeof f);
    for(int i=1;i<=m;i++) {
        Iscream(1,i);
        c[i]=b[1][i];
//		printf("%d %d\n", b[1][i].l,b[1][i].r);
    }
    if(flag) {
        int ans=0;
        for(int i=1;i<=m;i++) {
            ans+=!f[n][i];
        }
        printf("0\n%d\n",ans);
        return 0;
    }
    int L=1;
    sort(c+1,c+m+1);
    int ans=0;
    int j;
    for(int i=1;i<=m;i=j) {  //贪心策略
        if(c[i].l>L) break;
        ans++;
        j=i+1;
        int mx=c[i].r;
        for(;j<=m && c[j].l<=L;j++) {
            if(c[j].r<L) continue;
            mx=max(mx,c[j].r);
        }
        if(mx>=L) L=mx+1;
        if(L>m) break;
    }
    if(L<=m) {
        int ans=0;
        for(int i=1;i<=m;i++) {
            ans+=!f[n][i];
        }
        printf("0\n%d\n",ans);
        return 0;
    }
    printf("1\n%d\n", ans);
    return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值