电路维修(双端队列, 带图详解)

思路:将无需改变的边看成边权是0, 将需要改变的边看成边权是1.
本题可以看成一个弱化版的dijkstra求最短路,则可以采用双端队列,每次从当前点扩展别的点时,若扩展到的点边权是0则放对头,如果是1则放队尾。由于队头每次是边权最短的点,则最终点一定是最短路(原理同dijkstra, 每次取出边权最短的边)

原图是字符二维数组,可以把节点想象出来,不用存储
、、、、、、、、、、、、、、、、、
在这里插入图片描述

#include<iostream>
#include<cstdio>
#include<cstring>
#include<deque>
#define f first
#define s second
using namespace std;
typedef pair<int, int> PII;
const int N=550, INF=0x3f3f3f3f;
int bfs();

int T;
int n, m;
char g[N][N];//原字符串图
int dist[N][N];//距离
bool st[N][N];//出队时,当前点是否遍历过。原因是二图
int dx[4]={-1, -1, 1, 1}, dy[4]={-1, 1, 1, -1};//节点坐标
int ix[4]={-1, -1, 0, 0}, iy[4]={-1, 0, 0, -1};//字符串坐标
char op[]={"\\/\\/"};//左上,右上,左下, 右下
//三个方向数组一一对应

signed main()
{
    cin>>T;
    
    while(T--)
    {
        scanf("%d%d", &n, &m);
        for(int i=0;i<n;i++) scanf("%s", g[i]);
        
        if((n+m)&1)//如果终点横纵坐标之和是奇数,则不可能达到
        //这一性质,也决定了不会有,不在节点的交叉点.
        {
            puts("NO SOLUTION");
            continue;
        }
        
        printf("%d\n", bfs());
    }
}

int bfs()
{
    memset(dist, 0x3f, sizeof dist);
    memset(st, false, sizeof st);
    
    deque<PII> q;
    q.push_back({0, 0}), dist[0][0]=0;
    
    while(q.size())
    {
        auto t=q.front();
        q.pop_front();
        
        if(st[t.f][t.s]) continue;
        st[t.f][t.s]=true;
        //这里判断,而不在下面判断,是否遍历过,是因为
        //一个点的距离可能被更新多次,但这个点出队时一定是最短,推导为图2
        for(int i=0;i<4;i++)
        {
            int a=t.f+dx[i], b=t.s+dy[i];
            if(a<0||a>n||b<0||b>m) continue;
            
            int ca=t.f+ix[i], cb=t.s+iy[i];
            int d=dist[t.f][t.s]+(g[ca][cb]!=op[i]);//判断边权,即能走得线和实际的线是否相同,不同是1, 相同边权是0
            
            if(d<dist[a][b])
            {
                dist[a][b]=d;
                g[ca][cb]!=op[i]?q.push_back({a, b}):q.push_front({a, b});//相同放队头,否则放队尾
            }
        }
    }
    
    return dist[n][m];
}
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值