题目链接:https://www.acwing.com/solution/content/8249/
将两个结点之间能直接相连的边长视为0,不能直接相连的边长视为1,因为需要逆转,所以这个问题就变成了N*M个结点的最短路问题,可以通过双端队列搜索进行优化,
因为在队列中的点具有“两段性”和“单调性”,所以对于取出的点,如果以同样的值更新了一个点(i,j)那这个点的权值一定是双端队列中最小的(可以跟队首的某些一样,但觉度不会大于队首)。
由于队列中只会有两个层次的结果,也就是只会有两种值(“两段性”),所以队首+1后放到队尾也一定是满足两段性和单调性的。
问题中有一个需要注意的地方,就是一个点一旦被更新了之后可能还被更新,比如队首有两个点(i1,j1)和(i2,j2)他们的distance相同,但是队首的元素+1更新了一个点(x,y),第二个元素可能以
distance+0更新点(x,y),所以有必要对每个点都不断迭代。
注意:每个点第一次出队的时候一定就是最短的了,因为如果要更新他的最短路一定是通过队列中的其他点的,但是其他点的长度不比他短。
代码:
#include<iostream> #include<cstring> #include<deque> using namespace std; #define maxn 510 char s[maxn][maxn]; int dx[]={-1,-1,1,1},dy[]={-1,1,1,-1};//点的坐标偏移 int ix[]={-1,-1,0,0},iy[]={-1,0,0,-1}; char ss[]="\/\/"; int n,m; bool vis[maxn][maxn]; int d[maxn][maxn]; int bfs(){ deque< pair<int,int> > dq; dq.push_back({0,0});//将起点存入队列 memset(d,0x3f,sizeof(d)); d[0][0]=0; while(!dq.empty()){ pair<int,int> p=dq.front(); dq.pop_front(); int x=p.first,y=p.second; if(x==n && y==m)return d[x][y]; for(int i=0;i<4;i++){ int xx=x+dx[i]; int yy=y+dy[i]; if(xx<0 || xx>n || yy<0 || yy>m)continue; int px=x+ix[i]; int py=y+iy[i]; int w=0; if(s[px][py]!=ss[i])w=1;//输入与预期是垂直的,边长是1 if(d[xx][yy] > d[x][y]+w){ d[xx][yy]=d[x][y]+w; if(w)dq.push_back({xx,yy});//边长度为1的插入队尾,长度为0的插入队首 else dq.push_front({xx,yy}); } } } return d[n+1][m+1]; } int main(){ int T; cin>>T; while(T--){ cin>>n>>m; for(int i=0;i<n;i++)scanf("%s",s[i]); int ans=bfs(); if(ans==0x3f3f3f3f)cout<<"NO SOLUTION"<<endl; else cout<<ans<<endl; } }
加上一个vis之后的代码,使用双端队列可以对dijkstra算法至少优化掉一个log,使用双端队列也是对dijkstra中的heap的优化,主要是利用了题目的特性。
#include<iostream> #include<cstring> #include<deque> using namespace std; #define maxn 510 char s[maxn][maxn]; int dx[]={-1,-1,1,1},dy[]={-1,1,1,-1};//点的坐标偏移 int ix[]={-1,-1,0,0},iy[]={-1,0,0,-1}; char ss[]="\/\/"; int n,m; bool vis[maxn][maxn]; int d[maxn][maxn]; int bfs(){ deque< pair<int,int> > dq; dq.push_back({0,0});//将起点存入队列 memset(d,0x3f,sizeof(d)); d[0][0]=0; memset(vis,0,sizeof(vis)); while(!dq.empty()){ pair<int,int> p=dq.front(); dq.pop_front(); int x=p.first,y=p.second; if(vis[x][y])continue; vis[x][y]=1; if(x==n && y==m)return d[x][y]; for(int i=0;i<4;i++){ int xx=x+dx[i]; int yy=y+dy[i]; if(xx<0 || xx>n || yy<0 || yy>m)continue; int px=x+ix[i]; int py=y+iy[i]; int w=0; if(s[px][py]!=ss[i])w=1;//输入与预期是垂直的,边长是1 if(d[xx][yy] > d[x][y]+w){ d[xx][yy]=d[x][y]+w; if(w)dq.push_back({xx,yy});//边长度为1的插入队尾,长度为0的插入队首 else dq.push_front({xx,yy}); } } } return d[n+1][m+1]; } int main(){ int T; cin>>T; while(T--){ cin>>n>>m; for(int i=0;i<n;i++)scanf("%s",s[i]); int ans=bfs(); if(ans==0x3f3f3f3f)cout<<"NO SOLUTION"<<endl; else cout<<ans<<endl; } }