bfs + deque
bfs + deque
利用双端队列加速bfs的过程
P4554 小明的游戏
题目描述
小明最近喜欢玩一个游戏。给定一个n*m的棋盘,上面有两种格子#和@。游戏的规则很简单:给定一个起始位置和一个目标位置,小明每一步能向上,下,左,右四个方向移动一格。如果移动到同一类型的格子,则费用是0,否则费用是1。请编程计算从起始位置移动到目标位置的最小花费。
输入格式
输入文件有多组数据。
输入第一行包含两个整数n,m,分别表示棋盘的行数和列数。
输入接下来的nn行,每一行有m个格子(使用#或者@表示)。
输入接下来一行有四个整数x1,y1,x2,y2,分别为起始位置和目标位置。
当输入n,m均为0时,表示输入结束。
输入
2 2
@#
#@
0 0 1 1
2 2
@@
@#
0 1 1 0
0 0
输出
2
0
实现
bfs广搜
我们在用dijkstra跑最短路的时候,会用到堆优化,把当前最短路最小的点放到前面去。
现在这个图的权要么是0,要么是1,如果还用堆来维护,似乎有些浪费了。
这个时候我们就要用到双端队列,它支持查询队头、队尾,加入元素到队头、队尾。
我们仍然希望距离大的点放后面,小的放前面,该怎么办呢?
因为只有0和1两种权值,如果u→v的边是0权,那就把v放在队头;否则放在队尾。
现在原本\(\Theta(\log n)\)的堆优化,在这里只需要\(\Theta(1)\)了!
deque
代码
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <deque>
using namespace std;
typedef long long ll;
const int N=2e3;
struct node{
int x;
int y;
};
char c[N][N],s;
int dx[4]={-1,1,0,0};
int dy[4]={0,0,-1,1};
int dis[N][N];
int n,m,x1,x2,y1,y2;
bool vis[N][N];
void bfs()
{
deque<node> q;
memset(dis,0x3f,sizeof(dis));
memset(vis,0,sizeof(vis));
dis[x1][y1] = 0;
q.push_back((node){x1,y1});
while(!q.empty())
{
int x = q.front().x;
int y = q.front().y;
q.pop_front();
if(vis[x][y]) continue;
vis[x][y] = true;
for(int i=0;i<=3;i++)
{
int tx = x + dx[i];
int ty = y + dy[i];
if(tx < 1 || tx > n || ty < 1 || ty > m) continue;
int w;
if(c[tx][ty] == c[x][y]) w = 0;
else w = 1;
if(dis[tx][ty] < dis[x][y] + w) continue;
dis[tx][ty] = dis[x][y] + w;
if(w == 0) q.push_front((node){tx,ty});
else q.push_back((node){tx,ty});
}
}
}
int main()
{
while(1)
{
cin>>n>>m;
if(n == 0 && m == 0) break;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
cin>>c[i][j];
}
cin>>x1>>y1>>x2>>y2;
x1++;y1++;x2++;y2++;
bfs();
printf("%d\n",dis[x2][y2]);
}
return 0;
}
P4667 [BalticOI 2011 Day1]Switch the Lamp On
题目描述
正在设计电路。有一种正方形的电路元件,在它的两组相对顶点中,有一组会用导线连接起来,另一组则不会。有 \(N\times M\) 个这样的元件,你想将其排列成 \(N\) 行,每行 \(M\) 个。 电源连接到板的左上角。灯连接到板的右下角。只有在电源和灯之间有一条电线连接的情况下,灯才会亮着。为了打开灯,任何数量的电路元件都可以转动 90°(两个方向)
如果右边的第二列的任何一个电路元件被旋转 90°,电源和灯都会连接,灯被打开。现在请你编写一个程序,求出最小需要多少旋转多少电路元件。
输入格式
输入的第一行包含两个整数 \(N\) 和 \(M\),表示盘子的尺寸。 在以下 \(N\) 行中,每一行有 \(M\) 个符号 \ 或 /,表示连接对应电路元件对角线的导线的方向。
输出格式
如果可以打开灯,那么输出只包含一个整数,表示最少转动电路元件的数量。
输入
3 5
\\/\\
\\///
/\\\\
输出
1
思路
设转动一次边权设为1,不转动边权设为0,从左上到右下一定是能不转就不转,这样的边权才最小,0,1边权可以用bfs+deque实现,每次走对角线
代码
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <deque>
using namespace std;
typedef long long ll;
const int N=2e3;
struct node{
int x;
int y;
};
int dx[4] = {-1,-1,1,1};
int dy[4] = {-1,1,-1,1};
bool vis[N][N];
int dis[N][N];
char c[N][N];
int n,m,x,y;
void bfs()
{
deque<node> q;
memset(dis,0x3f,sizeof(dis));
memset(vis,0,sizeof(vis));
dis[x][y] = 0;
q.push_back(node{x,y});
while(!q.empty())
{
int nx = q.front().x;
int ny = q.front().y;
q.pop_front();
if(vis[nx][ny]) continue;
vis[nx][ny] = true;
for(int i=0;i<=3;i++)
{
int tx = nx + dx[i];
int ty = ny + dy[i];
int w = 1;
if(tx < 1 || tx > n+1 || ty < 1 || ty > m+1) continue;
if(c[nx][ny] == char(92) && i == 3) w = 0;//右下
if(c[nx-1][ny-1] == char(92) && i == 0) w = 0;//左上
if(c[nx][ny-1] == char(47) && i == 2) w = 0;//左下
if(c[nx-1][ny] == char(47) && i == 1) w = 0;//右上
if(dis[tx][ty] > dis[nx][ny] + w)
dis[tx][ty] = dis[nx][ny] + w;
if(w == 0) q.push_front((node){tx,ty});
else q.push_back((node){tx,ty});
}
}
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
cin>>c[i][j];
/*for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
cout<<c[i][j]<<" ";
printf("\n");
}*/
x = 1;y = 1;
bfs();
/*for(int i=1;i<=n+1;i++)
{
for(int j=1;j<=m+1;j++) printf("%d ",dis[i][j]);
printf("\n");
}*/
if(dis[n+1][m+1] == 1061109567) puts("NO SOLUTION");
else cout<<dis[n+1][m+1];
return 0;
}

浙公网安备 33010602011771号