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; }
阳光健康,就到这里吧。