zoukankan      html  css  js  c++  java
  • Luogu P1514引水入城【搜索】 By cellur925

    题目传送门

    这道题开始看好像并没有什么思路,和搜索好像也并没有什么关系。但是我们手玩下样例就会发现,思路其实就三句话:(写这道题的时候在代码里写的)

    • //我们想知道从第1行的每列往下到干旱区的范围

    • //要求这个直接bfs就行了

    • //然后就转换为了一个最小线段覆盖了

    写了个bfs,在学长的blog那里看了看线段覆盖怎么搞,于是就交了,开始是30分,因为bfs的时候我只用了3个方向。开始以为它是不能向上建水站的,后来想想其实也可以,只要满足有公共边+高度有落差就行了...再交就90分了$qwq$,T了一个点,开了氧气优化+快读,然后A了(???)

    // luogu-judger-enable-o2
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<queue>
    
    using namespace std;
    
    int n,m,ans,tot;
    int la[600][600],tong[600],fla[600];
    bool vis[600][600];
    int dx[5]={0,0,1,0,-1};
    int dy[5]={0,-1,0,1,0};
    struct segment{
        int l,r;
    }p[600];
    
    void re(int &x)
    {
        x=0;
        char ch=getchar();
        bool flag=false;
        while(ch<'0'||ch>'9') flag|=(ch=='-'),ch=getchar();
        while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
        x=flag ? -x : x;
    }
    
    bool valid(int x,int y)
    {
        if(x>=1&&x<=n&&y>=1&&y<=m) return 1;
        return 0;
    }
    
    bool cmp(segment x,segment y)
    {
        return x.l<y.l;
    }
    
    void bfs(int x,int y)
    {
        memset(vis,0,sizeof(vis));
        queue<pair<int,int> >q;
        q.push(make_pair(x,y));
        vis[x][y]=1;
        while(!q.empty())
        {
            int nx=q.front().first;
            int ny=q.front().second;
            q.pop();
            for(int i=1;i<=4;i++)
            {
                int xx=nx+dx[i];
                int yy=ny+dy[i];
                if(valid(xx,yy)&&la[nx][ny]>la[xx][yy]&&!vis[xx][yy])
                    q.push(make_pair(xx,yy)),vis[xx][yy]=1;
            }
        }
        int l=99999,r=0;
        for(int j=1;j<=m;j++)
            if(vis[n][j]) tong[y]++,fla[j]++,l=min(l,j),r=max(r,j);
        if(!tong[y]) return ;
        p[++tot].l=l,p[tot].r=r;
    }
    
    int main()
    {
        //我们想知道从第1行的每列往下到干旱区的范围 
        //要求这个直接bfs就行了
        //然后就转换为了一个最小线段覆盖了 
        re(n);re(m); 
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
                re(la[i][j]);
        for(int j=1;j<=m;j++)
            bfs(1,j);
    //	for(int j=1;j<=m;j++) printf("%d %d
    ",p[j].l,p[j].r);
        for(int j=1;j<=m;j++) 
            if(!fla[j]) ans++;
        if(ans) {printf("0
    %d",ans);return 0;}
        sort(p+1,p+tot+1,cmp);
        int s=1;
        for (int i=1;i<=m;i++) 
        {
            int r=0;
            for (int j=1;j<=m;j++) 
                if (p[j].l<=s&&p[j].r>=s) r=max(r,p[j].r);
            ans++;
            s=r+1;
            if (s>m) break;
        }
        printf("1
    %d",ans);
        return 0;
    }
    

    为什么会T呢?刚才我们的算法复杂度其实严格是$O(n3+nlogn+n2)$的。开始不解为什么搜索会是$O(n3)$,想了想后发现这是网格图,遍历所有点一遍是$O(n2)$的。而本题极限数据$500$,可能会跑到一亿,超时。

    刚才我们有最直观的想法:从第一行的每列出发向下搜索,但事实上我们可以不对所有第一行的位置进行向下搜索。只对那些满足$h[1][i]>=h[1][i-1]$而且$h[1][i]>=h[1][i+1]$的点进行搜索。因为当前点如果不满足这些条件,那么它形成的区间一定可以被其他区间覆盖,这个区间就没什么用了。

    // luogu-judger-enable-o2
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<queue>
    
    using namespace std;
    
    int n,m,ans,tot;
    int la[600][600],tong[600],fla[600];
    bool vis[600][600];
    int dx[5]={0,0,1,0,-1};
    int dy[5]={0,-1,0,1,0};
    struct segment{
        int l,r;
    }p[600];
    
    void re(int &x)
    {
        x=0;
        char ch=getchar();
        bool flag=false;
        while(ch<'0'||ch>'9') flag|=(ch=='-'),ch=getchar();
        while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
        x=flag ? -x : x;
    }
    
    bool valid(int x,int y)
    {
        if(x>=1&&x<=n&&y>=1&&y<=m) return 1;
        return 0;
    }
    
    bool cmp(segment x,segment y)
    {
        return x.l<y.l;
    }
    
    void bfs(int x,int y)
    {
        memset(vis,0,sizeof(vis));
        queue<pair<int,int> >q;
        q.push(make_pair(x,y));
        vis[x][y]=1;
        while(!q.empty())
        {
            int nx=q.front().first;
            int ny=q.front().second;
            q.pop();
            for(int i=1;i<=4;i++)
            {
                int xx=nx+dx[i];
                int yy=ny+dy[i];
                if(valid(xx,yy)&&la[nx][ny]>la[xx][yy]&&!vis[xx][yy])
                    q.push(make_pair(xx,yy)),vis[xx][yy]=1;
            }
        }
        int l=99999,r=0;
        for(int j=1;j<=m;j++)
            if(vis[n][j]) tong[y]++,fla[j]++,l=min(l,j),r=max(r,j);
        if(!tong[y]) return ;
        p[++tot].l=l,p[tot].r=r;
    }
    
    int main()
    {
        //我们想知道从第1行的每列往下到干旱区的范围 
        //要求这个直接bfs就行了
        //然后就转换为了一个最小线段覆盖了 
        re(n);re(m); 
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
                re(la[i][j]);
        for(int j=1;j<=m;j++)
            if(la[1][j]>=la[1][j-1]&&la[1][j]>=la[1][j+1])
    			bfs(1,j);
    //	for(int j=1;j<=m;j++) printf("%d %d
    ",p[j].l,p[j].r);
        for(int j=1;j<=m;j++) 
            if(!fla[j]) ans++;
        if(ans) {printf("0
    %d",ans);return 0;}
        sort(p+1,p+tot+1,cmp);
        int s=1;
        for (int i=1;i<=m;i++) 
        {
            int r=0;
            for (int j=1;j<=m;j++) 
                if (p[j].l<=s&&p[j].r>=s) r=max(r,p[j].r);
            ans++;
            s=r+1;
            if (s>m) break;
        }
        printf("1
    %d",ans);
        return 0;
    }
    
  • 相关阅读:
    jquery学会的
    oracle技巧-持续更新
    c语言技巧--长期更新
    2019暑假集训 最大子树和
    2019暑假集训 细胞分裂
    2019暑假集训 金明的预算方案
    2019暑假集训 能量项链
    2019暑假集训 神经网络
    0023-特殊的方程
    0022-并联电阻
  • 原文地址:https://www.cnblogs.com/nopartyfoucaodong/p/9864853.html
Copyright © 2011-2022 走看看