• Luogu 1514 引水入城 (搜索,动态规划)


    Luogu 1514 引水入城 (搜索,动态规划)

    Description

    在一个遥远的国度,一侧是风景秀美的湖泊,另一侧则是漫无边际的沙漠。该国的行政区划十分特殊,刚好构成一个N行M列的矩形,如上图所示,其中每个格子都代表一座城市,每座城市都有一个海拔高度。
    此处输入图片的描述
    为了使居民们都尽可能饮用到清澈的湖水,现在要在某些城市建造水利设施。水利设施有两种,分别为蓄水厂和输水站。蓄水厂的功能是利用水泵将湖泊中的水抽取到所在城市的蓄水池中。
    因此,只有与湖泊毗邻的第1行的城市可以建造蓄水厂。而输水站的功能则是通过输水管线利用高度落差,将湖水从高处向低处输送。故一座城市能建造输水站的前提,是存在比它海拔更高且拥有公共边的相邻城市,已经建有水利设施。由于第N 行的城市靠近沙漠,是该国的干旱区,所以要求其中的每座城市都建有水利设施。那么,这个要求能否满足呢?如果能,请计算最少建造几个蓄水厂;如果不能,求干旱区中不可能建有水利设施的城市数目。

    Input

    输入文件的每行中两个数之间用一个空格隔开。输入的第一行是两个正整数N 和M,表示矩形的规模。接下来N 行,每行M 个正整数,依次代表每座城市的海拔高度。

    Output

    输出有两行。如果能满足要求,输出的第一行是整数1,第二行是一个整数,代表最少建造几个蓄水厂;如果不能满足要求,输出的第一行是整数0,第二行是一个整数,代表有几座干旱区中的城市不可能建有水利设施。

    Sample Input

    2 5
    9 1 5 4 3
    8 7 6 1 2

    Sample Output

    1
    1

    Http

    Luogu:https://www.luogu.org/problem/show?pid=1514

    Source

    搜索,动态规划

    解决思路

    首先要找到一个顶部的城市能够最多流向底部的哪些城市。如果有解的话,这个底部的城市一定是一个区间。首先证明这一点
    如果顶部蓝色城市只能覆盖黄色的两部分
    此处输入图片的描述
    如果有解的话,那么中间的红色部分由这个绿色城市来覆盖
    此处输入图片的描述
    那既然绿色能够覆盖红色,蓝色为什么就不可以呢?可以发现箭头有交叉。
    所以我们发现,如果有解的话,顶部的一个城市一定对应一个底部城市的区间,否则一定无解。
    那么第一步就是从每一个顶部城市出发,向下一直走到最后一排寻找出那个区间,这里使用bfs实现。这里需要注意的是,如果每一个顶部城市都进行一遍bfs,势必会超时,所以一个剪枝就是如果这个城市的左边或右边海拔比它高,则这个城市不需要bfs,也就是说我们只对那些比它两边都高的城市bfs。因为海拔高的城市在bfs时一定会覆盖海拔低的相邻城市。
    那么接下来,问题就转化为给出若干个区间,选择其中最少的覆盖整个区间。
    笔者一开始采用的是排序贪心,然后用差分法标记,贪心地选择,但无奈一直没有调对,只好采用动态规划的方法。
    具体是设F[i]表示从左向右覆盖到城市i时所用的最少区间数,那么对于一个区间[l,r]而言,有(jin [l,r],F[j]=min(F[j],F[l-1]+1))

    代码

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    using namespace std;
    
    const int maxN=600;
    const int F1[5]={0,1,-1,0,0};//两个F数组分别是在bfs搜索时的四个方向
    const int F2[5]={0,0,0,1,-1};
    const int inf=2147483647;
    
    class Range//区间
    {
    public:
        int l,r;
    };
    
    class Pos//位置,bfs里要用
    {
    public:
        int x,y;
    };
    
    bool operator <(Range A,Range B)//之前贪心算法时要用到排序
    {
        return (A.l<B.l)||((A.l==B.l)&&(A.r>B.r));
    }
    
    int n,m;
    int Rangecnt=0;//统计总共找到了多少个可行区间
    int Height[maxN][maxN];//海拔
    bool vis[maxN][maxN];//bfs中标记是否走过
    bool is_cover[maxN];//标记最后一排城市是否走过,方便判断无解
    Range R[maxN];//区间
    queue<Pos> Q;//bfs队列
    int F[maxN];//最后求最少区间覆盖时动态规划
    
    void bfs(int st);
    
    int main()
    {
        memset(is_cover,0,sizeof(is_cover));//初始化
        Rangecnt=0;
        scanf("%d%d",&n,&m);
        for (int i=1;i<=n;i++)
            for (int j=1;j<=m;j++)//读入海拔
                scanf("%d",&Height[i][j]);
        Height[1][0]=0;//为了方便判断,在第一个城市左边和第二个城市右边加入高度为0的城市
        Height[1][m+1]=0;
        for (int i=1;i<=m;i++)//对于第一行中满足比它两边都高的城市进行bfs搜索区间
            if ((Height[1][i]>=Height[1][i-1])&&(Height[1][i]>=Height[1][i+1]))
                bfs(i);
        int sum=0;//sum用来统计总共覆盖了多少城市,如果sum!=m,则无解
        for (int i=1;i<=m;i++)
            sum+=(int)(is_cover[i]);
        if (sum<m)
        {
            printf("0
    %d
    ",m-sum);
            return 0;
        }
        sort(&R[1],&R[Rangecnt+1]);//其实这个排序是不必要的
        memset(F,126,sizeof(F));
        F[0]=0;
        for (int i=1;i<=Rangecnt;i++)//动态规划求解最少区间覆盖
            for (int j=R[i].l;j<=R[i].r;j++)
                F[j]=min(F[j],F[R[i].l-1]+1);
        printf("1
    %d
    ",F[m]);
        return 0;
    }
    
    void bfs(int st)//bfs求解
    {
        memset(vis,0,sizeof(vis));
        Pos init;
        init.x=1;
        init.y=st;
        vis[1][st]=1;
        while (!Q.empty())
            Q.pop();
        Q.push(init);
        do
        {
            Pos u=Q.front();
            Q.pop();
            for (int i=1;i<=4;i++)
            {
                int x=u.x+F1[i];
                int y=u.y+F2[i];
                if ((x<=n)&&(x>=1)&&(y<=m)&&(y>=1)&&(Height[u.x][u.y]>Height[x][y])&&(vis[x][y]==0))
                {
                    Q.push((Pos){x,y});
                    vis[x][y]=1;
                }
            }
        }
        while (!Q.empty());
        int l=-1,r=-1;//记录覆盖区间
        for (int i=1;i<=m;i++)
        {
            if ((vis[n][i]==1)&&(l==-1))
                l=i;
            if ((vis[n][i]==0)&&(l!=-1)&&(r==-1))
                r=i-1;
            if (vis[n][i]==1)//同时更新is_cover数组
                is_cover[i]=1;
        }
        if (l==-1)//可能出现当前选择的最上面一排的城市无法到达最下面一排,此时直接退出
            return;
        if (r==-1)//当r==-1说明覆盖到了最后一个城市
            r=m;
        Rangecnt++;
        R[Rangecnt].l=l;
        R[Rangecnt].r=r;
        return;
    }
    
  • 相关阅读:
    tkinter中entry输入控件(四)
    tkinter中button按钮控件(三)
    tkinter中lable标签控件(二)
    tkinter简介(一)
    selenium中的xpath定位
    python实现邮件的发送
    python发送手机动态验证码
    selenium提供的截图功能
    selenium中浏览器及对应的驱动(可下载)
    PHP实现微信提现功能
  • 原文地址:https://www.cnblogs.com/SYCstudio/p/7469285.html
Copyright © 2020-2023  润新知