电路维修-双端队列算法实现

博客围绕网格图路径搜索问题展开,最初考虑裸spfa算法,但网格图会卡spfa,需进行优化。因边权为1或0,采用双端队列BFS,优先搜索增加代价为0的点,保证每次扩展到一处就是当前最优解,且无需记忆化,时间复杂度为O(R∗C),相比优先队列解法更优。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目传送门:

https://www.luogu.org/problemnew/show/P2243

解题思路:

      \text{ \ \ \ \ }      判断有无解不用说了。
      \text{ \ \ \ \ }      首先想到裸 s p f a spfa spfa,以格点为点,若不用旋转则建权为0的边;若要旋转则建权为1的边。但网格图会卡 s p f a spfa spfa,那么我们就要进行优化。

      \text{ \ \ \ \ }      因为边权为1或0,所以我们可以利用双端队列 B F S BFS BFS,枚举所有相邻点,优先搜索增加代价为0的点,即将增加代价为0的点从队头入队;增加代价为1的点从队尾入队。

      \text{ \ \ \ \ }      并且要注意应该扩展到了当前点才更新v数组,否则会先扩展非最优解,而最优解无法扩展。 所以,你可以入队,但我不一定选你。

      \text{ \ \ \ \ }      这样一来,可以保证每次扩展到一处就是当前最优解。最开始是最优,又是先权少的路,单调下去。

     \text{ \ \ \ }     大吼一声:我大双端队列法不需要记忆化,时复 O ( R ∗ C ) O(R*C) O(RC)

      {\ \ \ \ \ }      其实优先队列的解法也类似,不过它需要记忆化,因为堆顶的边权最小但总代价不一定最小。从而不够优秀。总时间1470ms,而双端队列只要229ms。

上代码:

#include<cstdio>
#include<queue>
using namespace std;
char st[510][510];
bool v[510][510];
int n,m;
int minn(int x,int y){ return x<y?x:y; }
struct node{int x,y,c;}now; 
deque<node> q; 
void check(int x,int y,int c,int sx,int sy,char s)
{	
	if(x>=0 && x<=n && y>=0 && y<=m && !v[x][y])
	{
		if(st[sx][sy]==s) q.push_front((node){x,y,c}); 
		else q.push_back((node){x,y,c+1}); 
	}
}
int bfs()
{
	for(int i=0;i<=n;i++) for(int j=0;j<=m;j++) v[i][j]=false;
	while(!q.empty()) q.pop_front(); 
	node s;  s.x=0; s.y=0; s.c=0;
	q.push_front(s); 
	int nx,ny; 
	while(!q.empty())
	{
		now=q.front(); q.pop_front();
		int x=now.x , y=now.y;
		if(v[x][y]) continue;  v[x][y]=true;
		//应该扩展到了才更新,否则会先扩展非最优解 
		if(x==n && y==m) return now.c;	
		check(x+1,y+1,now.c,x+1,y+1,'\\');
		check(x-1,y-1,now.c,x,y,'\\');
		check(x+1,y-1,now.c,x+1,y,'/');
		check(x-1,y+1,now.c,x,y+1,'/');
		/* 因为点为格点,而输入却为格子,需要它们的转换关系。
		画图得此关系:格点+1->格子也+1 ; 格点-1 ,格子还是格子 */
	}
}
int main()
{
	int T;
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d %d",&n,&m);
		for(int i=1;i<=n;i++) scanf("%s",st[i]+1);	
		if((n+m)%2!=0) printf("NO SOLUTION\n");  
		else printf("%d\n",bfs());
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值