电路维修(双端队列广搜)

ACwing链接:https://www.acwing.com/problem/content/177/

对于边权只有0或者1的图来说,其更适合用双端队列来做

双端队列,其实是延续了dijkstra求最短路的思维,把dist较小的点放到队列前来更新其他点,效率会更高

对于此比若说题而言,什么时候图中边的边权为1,什么时候为0呢?

当从起点开始若不需要改变电路板就可以走到下一个点,那么边权自然为0,反之边权为1

但是这个题比较特殊,需要用字符数组模拟下两点之间“连线”的情况(实际上就是提前存好的电路板结构嘛

但是需要注意的一点是图中的点是n*m,但是点点之间连线要少一维,共(n-1)*(m-1)条线

所以我们需要开两个方向数组来模拟,一个数组来模拟点周围点的情况,另一个数组来模拟经过了哪些方格

建立方格的数组有点难以理解,我解释一下,我们知道刚开始所建点和所建方格的坐标是一样的,比如说(0,0)是第左上角第一个点,也是第一个方格,那么我从(0,0)点走到(1,1)的时候经过了哪个方格呢?自然还是(0,0)这个方格,所以这时候我们移动时候的方格的坐标是没有变化的,所以移动方格的坐标需要观察格点坐标和方格坐标的对应关系

而且由于我们只能斜着走方格,所以不同于很多题目惯用的“上下左右”走法

这里直接给出格点方向数组和格子方向数组:

int go_point[4][2] = {{-1,-1},{-1,1},{1,1},{1,-1}}; //格点走法
int go_cell[4][2] = {{-1,-1},{-1,0},{0,0},{0,-1}};  //格子走法

但是我们还需要一个模拟数组来表示两点之间的连边关系

需要注意的是得按照顺序对应写出线条的朝向,比方说坐标若是向左上方移动则线条的形状就是斜向右下方

char cs[5] = "\\/\\/"; 

具体流程就是我们每次输入一次图形就进行一次bfs,若是最后得出终点距离为正无穷,那么就说明无法到达,就要输出对应的字符串,否则就要输出具体的距离

到了bfs中,我们需要注意的是此题与一般的图不同的是我们需要用横纵坐标来表示具体的格点,因此判重st数组和距离数组都是二维的

我们先把起点(0,0)放入队列中,并且设置刚开始的这个点的距离为0

然后我们就开始while(q.size())并循环,每次取出队头来更新其他的点,并且注意一定是把队头给pop出去

取出队头,若是发现已经搜过了就continue,若是没有搜过就需要标记一下

然后就用for循环枚举队头这个点的四个方向,需要保证若是格点超出了图,就直接continue

选好方向以后还要看这个方向上的斜线是不是与将要走的方向相同,这个时候就要用到格点方向数组了

若是发现图上格子的斜线与将要走的方向不同,那么距离就要变成1,否则就是0,然后用队头点的距离加上朝向方向将要走的距离来更新目标点的距离值

更新之后,若走到这个点只需距离0,就放到队头,否则就放到队尾

最后return dist[n][m],也就是终点的距离

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<deque> 
using namespace std;
typedef pair<int,int>PII; 
const int N = 2520;
int n,m,w;
char g[N][N];
int dist[N][N];
bool st[N][N];
char cs[5] = "\\/\\/";              //用字符串定义字符数组 
int go_point[4][2] = {{-1,-1},{-1,1},{1,1},{1,-1}};
int go_cell[4][2] = {{-1,-1},{-1,0},{0,0},{0,-1}};
int bfs()
{
	memset(dist,0x3f3f3f3f,sizeof dist);
	memset(st,0,sizeof st);
	dist[0][0] = 0;
	deque<PII>q;
	q.push_back({0,0});
	while(q.size())
	{
		PII t = q.front();
		q.pop_front();                //把队头给弹出去 
		if(st[t.first][t.second]) continue;
		st[t.first][t.second] = 1;
		for(int i=0;i<4;i++)
		{
			int a = t.first+go_point[i][0];       //a和b是用用来枚举格点的 
			int b = t.second+go_point[i][1];
			if(a<0||a>n||b<0||b>m) continue;
			int ca = t.first+go_cell[i][0];
			int cb = t.second+go_cell[i][1];
			int d = dist[t.first][t.second] + (g[ca][cb]!=cs[i]);
			if(d<dist[a][b])
			{
				dist[a][b] = d;
				if(g[ca][cb]!=cs[i]) q.push_back({a,b});
				else q.push_front({a,b});
			}		 
		}
	} 
	return dist[n][m];
}
int main()
{
	cin>>w;
	while(w--)
	{
		cin>>n>>m;
		for(int i=0;i<n;i++) scanf("%s",g[i]);
		int t =  bfs();
		if(t==0x3f3f3f3f) cout<<"NO SOLUTION"<<endl;
		else cout<<t<<endl;
	}
	return 0;
} 

要加油啊!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值