zoukankan      html  css  js  c++  java
  • P4554 小明的游戏 (洛谷) 双端队列BFS

    最近没有更新博客,全是因为英语,英语太难了QWQ

    洛谷春令营的作业我也不会(我是弱鸡),随机跳了2个题,难度不高,还是讲讲吧,学学新算法也好(可以拿来水博客)

    第一题就是这个小明的游戏

    小明最近喜欢玩一个游戏。给定一个 n×m的棋盘,上面有两种格子#和@。游戏的规则很简单:给定一个起始位置和一个目标位置,小明每一步能向上,下,左,右四个方向移动一格。如果移动到同一类型的格子,则费用是0,否则费用是1。请编程计算从起始位置移动到目标位置的最小花费。
    
    输入格式
    
    输入文件有多组数据。
    输入第一行包含两个整数n,m,分别表示棋盘的行数和列数。
    输入接下来的n行,每一行有m个格子(使用#或者@表示)。
    输入接下来一行有四个整数x1, y1, x2, y2 
    分别为起始位置和目标位置。
    当输入n,m均为0时,表示输入结束。
    
    输出格式
    
    对于每组数据,输出从起始位置到目标位置的最小花费。每一组数据独占一行。
    

    看起来像个广搜,但是我们仔细想想的话会发现,这玩意是有权值的,可能我走100格用的费用比你走一格用的都少,在最少费用的要求下,显然不能用普通的广搜。

    广搜的特点是把所有能走到的点全加进队列,为什么会用这种算法呢?,因为每走一步就需要一点费用的情况下,这样走花费最少。

    于是,我们可以想出一个主意,我们让队列双开口,把花费小的放在前面,花费大的放后面岂不美哉。但有的同学可能会疑惑(其实只有我),这样的话不是要手打堆排?不不不,里面最多有2种数,因为我们去查看大数的情况前先要看小数的情况,小数只能变成自己或者自己+1,如果是自己+1,变成大数,放在最后。如果不是,放在前面再来一遍。所以这个队列里只会有2种数。不用担心排序的问题。

    再来几个小提示就贴代码了:

    1、队列记得清空

    2、记得标记来过没

    3、有个东西叫deque,deque支持高效插入和删除容器的头部和尾部元素,因此也叫做双端队列(我用的就是这个)

    #include<iostream>
    #include<cstdio>
    #include<deque>
    using namespace std;
    char sz[505][505];
    long long a[500][500];
    long long n,m,qx,qy,zx,zy;
    long long fx[4]={0,0,1,-1};//控制移动的数组 
    long long fy[4]={1,-1,0,0};
    deque<int>qz;//3个双端队列 
    deque<int>xd;
    deque<int>yd;
    long long x,y,z;
    void sddl()
    {
        while(xd.empty()!=true&&yd.empty()!=true)//不空不走 
        {
            x=xd.front();//这个是获取队列的头部元素 
            y=yd.front();
            z=qz.front();
            if(x==zx&&y==zy)
            {
            	while (xd.empty()!=true)xd.pop_front();//清空队列,我之前因为没清空疯狂80分 
            	while (yd.empty()!=true)yd.pop_front();
            	while (qz.empty()!=true)qz.pop_front();
                cout<<z<<endl;
                return;
            }
            xd.pop_front();
            yd.pop_front();
            qz.pop_front();
            for(int i=0;i<4;i++)
            {
                if(x+fx[i]>=0&&x+fx[i]<n&&y+fy[i]>=0&&y+fy[i]<m)//判断越界 
                {
                    if(a[x+fx[i]][y+fy[i]]==0)//标记来过没 
                    {
                        a[x+fx[i]][y+fy[i]]=1;
                        if(sz[x+fx[i]][y+fy[i]]==sz[x][y])//走这个不需要花费,走起 
                        {
                            xd.push_front(x+fx[i]);//这个是插入到头部的意思 
                            yd.push_front(y+fy[i]);
                            qz.push_front(z);
                        }else//花费1点 
                        {
                            xd.push_back(x+fx[i]);//这个是插入到尾部的意思 
                            yd.push_back(y+fy[i]);
                            qz.push_back(z+1);
                        }  
                    }
                }
            }
        }
        return;
    }
    int main()
    {
        while(true)
        {
            scanf("%lld%lld",&n,&m);
            if(n==0&&m==0)
            {
                return 0;
            }
            for(int i=0;i<n;i++)
            {
                for(int j=0;j<m;j++)
                {
                    cin>>sz[i][j];
                    a[i][j]=0;
                }
            }
            scanf("%lld%lld%lld%lld",&qx,&qy,&zx,&zy);
            a[qx][qy]=1;//省一下 
            xd.push_front(qx);//这个是插入到头部的意思 
            yd.push_front(qy);
            qz.push_front(0);
            sddl();
        }
        return 0;
    }
    

    阳光健康,就到这里吧。

  • 相关阅读:
    C#中 @ 的用法
    ASP.NET页面间传值
    ASP.NET中常用的文件上传下载方法
    把图片转换为字符
    把图片转换为字符
    JavaScript 时间延迟
    Using WSDLs in UCM 11g like you did in 10g
    The Definitive Guide to Stellent Content Server Development
    解决RedHat AS5 RPM安装包依赖问题
    在64位Windows 7上安装Oracle UCM 10gR3
  • 原文地址:https://www.cnblogs.com/lichangjian/p/13030221.html
Copyright © 2011-2022 走看看