[NOIP2010]引水入城 记忆化搜索,树状数组

82 篇文章 1 订阅
26 篇文章 0 订阅

This way

题意:

在这里插入图片描述

题解:

我们可以发现,从每一个点出发都是一棵树,也就是对于任意一个点,它的决策是一定的,状态不会改变。那么很明显是记忆化搜索
然后有一个很明显的性质,如果某个点开始,到达的区间不是连续的,那么中间这部分一定是高出旁边的,所以无论怎么走都不行。
那么dp[i][j]表示第i行j列开始能走的最大区间左右端点。
将所有都求出来之后,找最小的线段数量用树状数组即可

#include<bits/stdc++.h>
using namespace std;
#define pa pair<int,int>
const int N=505;
int mp[N][N],vis[N];
int cnt;
int xmov[]={1,0,-1,0};
int ymov[]={0,1,0,-1};
int n,m;
int can[N];
pa dp[N][N];
pa dfs(int x,int y){
    if(dp[x][y]!=(pa){-1,-1})
        return dp[x][y];
    int f=0;
    pa ans={0,0};
    vector<pa>vec;
    for(int i=0;i<4;i++){
        int nx=xmov[i]+x,ny=y+ymov[i];
        if(nx<1||nx>n||ny<1||ny>m||mp[nx][ny]>=mp[x][y])continue;
        f=1;
        pa v=dfs(nx,ny);
        if(v==(pa){0,0})continue;
        vec.push_back(v);
    }
    if(x==n)
        vec.push_back({y,y}),can[y]=1;
    f=0;
    if(!vec.empty()){
        sort(vec.begin(),vec.end());
        ans=*vec.begin();
        for(int i=1;i<vec.size();i++){
            if(ans.second<vec[i].first-1){
                ans={0,0};
                break;
            }
            ans.second=max(ans.second,vec[i].second);
        }
    }
    dp[x][y]=ans;
    return ans;
}
int l[N],r[N],num[N],mx[N];
int lowbit(int x){return x&(-x);}
void add(int x,int v){
    for(int i=x;i<N;i+=lowbit(i))
        mx[i]=max(mx[i],v);
}
int query(int x){
    int ans=0;
    for(int i=x;i;i-=lowbit(i))
        ans=max(ans,mx[i]);
    return ans;
}
int main()
{
    memset(dp,-1,sizeof(dp));
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            scanf("%d",&mp[i][j]);
    for(int i=1;i<=m;i++){
        pa v=dfs(1,i);
        if(v==(pa){0,0})continue;
        add(v.first,v.second);
    }
    int sum=0;
    for(int i=1;i<=m;i++)
        if(!can[i])
            sum++;
    if(sum)return 0*printf("0\n%d\n",sum);
    int ans=0;
    int p=0;
    while(p<m){
        p=query(p+1);
        ans++;
        //if(ans>500)return 0*printf("0\n");
    }
    printf("1\n%d\n",ans);
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值