NOIP2010 T4 引水入城

本文介绍了一种结合宽度优先搜索与贪心策略的算法,用于解决特定类型的路径覆盖问题。通过对第一行各点进行搜索并统计到达最后一行的情况,确定最少线段覆盖范围。文章详细阐述了算法实现思路与优化技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

宽搜+贪心
还是挺容易的
对第一行的每一个点进行搜索,统计最后一行每个点的到达情况
然后记录下该点可到达的最左端和最右端的位置
判断最后一行的到达情况,统计不可到达的点,如果不为0就输出,退出程序
否则就是做最少线段覆盖问题
可以证明第一行的每一个点可到达最后一行的位置是连续的(我证不来)
问题就变成了完全覆盖1m最少要几条线段
把线段以头为主要关键字从小到大,尾为次要关键字从大到小排序
设当前线段可覆盖1x,我们要找的下一条线段是头在1x中,尾最大的线段,直到全部覆盖为止
vijos1A了,然而落谷上T了一个点,所以加个优化,当第一行某个点不低于左右俩个点时才搜索


#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define g getchar()
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
inline ll read(){
    ll x=0,f=1;char ch=g;
    for(;ch<'0'||ch>'9';ch=g)if(ch=='-')f=-1;
    for(;ch>='0'&&ch<='9';ch=g)x=x*10+ch-'0';
    return x*f;
}
inline void out(ll x){
    int a[25],t=0;
    if(x<0)putchar('-'),x=-x;
    for(;x;x/=10)a[++t]=x%10;
    for(int i=t;i;--i)putchar('0'+a[i]);
    if(t==0)putchar('0');
    putchar('\n');
}
struct re{int x,y;}s[505],dui[505*505];
int n,m,a[505][505],tou,wei,ans,now,nx;
bool mp[505][505],pd[505];
int ox[5]={0,1,0,-1,0},oy[5]={0,0,1,0,-1};
inline bool cmp(re x,re y){return x.x==y.x?x.y<y.y:x.x<y.x;}
int main(){
//  freopen("1.in","r",stdin);
//  freopen("","w",stdout);
    n=read();m=read();
    memset(a,inf,sizeof(a));
    for(int i=1;i<=n;++i)
    for(int j=1;j<=m;++j)
    a[i][j]=read();
    memset(s,inf,sizeof(s));
    for(int i=1;i<=m;++i){
        if(!((i==1||i==m)||(a[1][i]>=a[1][i-1]&&a[1][i]>=a[1][i+1])))continue;
        dui[tou=wei=1]=(re){1,i};
        memset(mp,0,sizeof(mp));mp[1][i]=1;
        for(;tou<=wei;++tou){
            int x=dui[tou].x,y=dui[tou].y;
            for(int j=1;j<=4;++j){
                if(mp[x+ox[j]][y+oy[j]])continue;
                if(a[x][y]<=a[x+ox[j]][y+oy[j]])continue;
                mp[x+ox[j]][y+oy[j]]=1;
                dui[++wei]=(re){x+ox[j],y+oy[j]};
            }
        }
        for(int j=1;j<=m;++j){
            pd[j]=(pd[j]||mp[n][j]);
            if(mp[n][j]&&!mp[n][j-1])s[i].x=j;
            if(mp[n][j]&&!mp[n][j+1])s[i].y=j;
        }
    }
    for(int i=1;i<=m;++i)ans+=pd[i];
    if(ans<m){out(0),out(m-ans);return 0;}
    sort(s+1,s+1+m,cmp);
    now=1;nx=0;ans=0;
    for(;now<=m;){
        int i,tmp=0;
        for(i=1;i<=m;++i){
            if(now<s[i].x)break;
            if(tmp<s[i].y)tmp=s[i].y;
        }
        now=tmp+1;++ans;
    }
    out(1),out(ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值