复制代码

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;
}

posted @ 2021-10-06 19:53  Elgina  阅读(94)  评论(0)    收藏  举报