这是一道在luogu的蓝题,在yxc大佬的讲解下AC掉了(百般调试)
首先这道题给了一个字符串矩阵,/ 表示相连哪两个节点,只可以走/ 所连接的两个点,但我们可以旋转每一个边,询问从1,1 走到 n+1,m+1的最小旋转次数。如果到不了就输出no。
首先我们要明确点的坐标和对于点四周的格子上边的坐标。其次我们考虑算法,发现这个边权一个是0,一个是1,并且要搜最小步数,我们考虑狄杰斯特拉发现是可行的,所以这也证明了bfs是正确的,然后对于这种问题我们采用stl_deque来写。对于deque,我们对于每一个点进行dijksra式的扩展,假如这个点作为过front,那么打标记,但入队后不需要打标记,和普通的队列不一样,因为这里存在多次进队的可能。然后运用四个方向数组以及一个正确匹配的边进行判断权值是1还是0,松弛dist[tx][ty],假如松弛成功,我们将其入队,边权为0放到front,边权为1放到back。最后就是特判,假如终点的横纵坐标为奇数,肯定到不了。
1.对于格子图和块图要分清楚,方向数组写好,最后答案,越界判断细心写
2.多组数据,千万别return 0了,特判的放在输入后面,还有memset
3.deque: 权0前1后,打当过head的点标记
代码
#include<bits/stdc++.h> #define maxn 505 using namespace std; char mp[maxn][maxn]; bool st[maxn][maxn]; int dist[maxn][maxn]; int n,m; int xx[maxn],yy[maxn]; int T; /*int dx[4]={-1,1,-1,1}; int dy[4]={-1,-1,1,1}; int ix[4]={-1,-1,0,0}; int iy[4]={-1,0,-1,0}; char cs[]="\//\";*/ char cs[] = "\/\/"; 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}; struct node{ int x,y; }; int bfs(){ memset(dist,0x3f,sizeof(dist)); memset(st,false,sizeof(st)); dist[1][1]=0; deque<node>q; q.push_back({1,1}); while(q.size()){ node a=q.front(); q.pop_front(); if(st[a.x][a.y]==true) continue;//只可以被当做一次堆顶 st[a.x][a.y]=true; for(int i=0;i<=3;i++){ int tx=a.x+dx[i]; int ty=a.y+dy[i]; if(tx<1||ty<1||tx>n+1||ty>m+1) continue; int gx=a.x+ix[i]; int gy=a.y+iy[i]; int w=0; if(mp[gx][gy]!=cs[i]){ w=1; } int d=w+dist[a.x][a.y]; if(d<dist[tx][ty]){ dist[tx][ty]=d; if(w==1) q.push_back({tx,ty});//权为1,后插 else q.push_front({tx,ty});//权为0,前插 } } } return dist[n+1][m+1]; } int main(){ cin>>T; while(T--){ cin>>n>>m; for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ cin>>mp[i][j]; } } if((n+m)%2!=0){ cout<<"NO SOLUTION"<<endl; continue; } cout<<bfs()<<endl; } return 0; }