173. 矩阵距离(173. 矩阵距离 - AcWing题库)
思路:这题和多源最短路还是有一些区别的,相当于求一个点到距离它最近的一个终点的距离,其实我们可以反着来看,将终点视为起点,然后加一个虚拟起点,将这些起点到虚拟起点的距离设为0,那么我们实际上要求的就是所有点到虚拟起点的最短路。这里这个虚拟起点并不需要真实存在,我们将所有起点同时放进队列即可。
#include<bits/stdc++.h>
using namespace std;
#define x first
#define y second
typedef pair<int,int> pii;
pii q[1000010];
int d[1010][1010];
char s[1010][1010];
int n,m,hh,tt;
int dx[]={0,0,1,-1};
int dy[]={1,-1,0,0};
void bfs()
{memset(d,-1,sizeof d);hh=tt=0;for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){if(s[i][j]=='1'){d[i][j]=0;q[tt++]={i,j};}}}while(hh<tt){auto t=q[hh++];for(int i=0;i<4;i++){int nx=t.x+dx[i],ny=t.y+dy[i];if(nx<1||nx>n||ny<1||ny>m) continue;if(d[nx][ny]!=-1) continue;d[nx][ny]=d[t.x][t.y]+1;q[tt++]={nx,ny};}}
}
int main()
{scanf("%d%d",&n,&m);for(int i=1;i<=n;i++) scanf("%s",s[i]+1);bfs();for(int i=1;i<=n;i++){for(int j=1;j<=m;j++)printf("%d ",d[i][j]);printf("\n");}
}
1107. 魔板(1107. 魔板 - AcWing题库)
思路:这道题和之前遇到的题目不同的地方就在于之前的是在一张图中进行查找,而这里则是将每一张图视为一个状态。求一个状态到另一个状态的最小步数。所以要解决的第一个问题就是如何存状态,我们可以将二维的矩阵转化成一个字符串,然后在操作的时候再转化回去,将操作之后的状态再转化成一个字符串。另外我们再来看看这里要求的量,一个是求最小的操作次数,另一个是求字典序最小的操作序列,这里为了得到字典序最小的操作序列,实际上我们按照ABC的顺序来更新,再将更新到的序列放入队列即可。根据队列的单调性可知,这样得到的终点操作数一定是最小的。为了得到操作方案,我们只用记录一下每一步是由谁转化而来的即可。
#include<bits/stdc++.h>
using namespace std;
char g[2][4];
unordered_map<string,int>d;
unordered_map<string,pair<char,string>>pre;
void tog(string state)
{for(int i=0;i<4;i++) g[0][i] = state[i];for(int i=7,j=0;j<4;i--,j++) g[1][j]=state[i];}
string tos()
{string res;for(int i=0;i<4;i++) res += g[0][i];for(int i=3;i>=0;i--) res+=g[1][i];return res;
}
string move0(string state)
{tog(state);for(int i=0;i<4;i++) swap(g[0][i],g[1][i]);return tos();
}
string move1(string state)
{tog(state);char a=g[0][3],b=g[1][3];for(int i=3;i>0;i--) {g[0][i]=g[0][i-1];g[1][i]=g[1][i-1];}g[0][0]=a,g[1][0]=b;return tos();
}
string move2(string state)
{tog(state);char a=g[0][1];g[0][1]=g[1][1];g[1][1]=g[1][2];g[1][2]=g[0][2];g[0][2]=a;return tos();
}
int bfs(string state,string end)
{if(state==end) return 0;queue<string>q;q.push(state);d[state]=0;while(q.size()){auto t=q.front();q.pop();string m[3];m[0]=move0(t);m[1]=move1(t);m[2]=move2(t);for(int i=0;i<3;i++)if(!d.count(m[i])){d[m[i]]=d[t]+1;pre[m[i]]={'A'+i,t};q.push(m[i]);if(m[i]==end) return d[end];}}return -1;
}
int main()
{int x;string s,e;for(int i=0;i<8;i++){cin>>x;e+=char(x+'0');}s="12345678";int step=bfs(s,e);cout<<step<<endl;string res;while(e!=s){res += pre[e].first;e=pre[e].second;}reverse(res.begin(),res.end());if(step>0)cout<<res;
}
对了,一定要注意一下,是从谁到谁。‘
175. 电路维修(175. 电路维修 - AcWing题库)
思路:
按照这个图就很明显,每个点只有四个点是可以到的,那么我们就来思考一下如果这四个方格中的线的路径与给定的相同,那么显然就不用操作,否则就要进行一次操作,所以我们到下一个点的边权可能是1,也可能是0,为了保证队列的单调性就不能直接放队尾,那么我们可以直接令为0的时候从队头插入,为1的时候从队尾插入,那么可以用双端队列实现。
然后再分析一下点的格子的关系,因为我们给的是每个格子的状态,但我们实际更新的状态是各个点的状态。
那么就出来了,某点将要转移到的四个点
dx[]={-1,-1,1,1}
dy[]={-1,1,-1,1}
用到的格子:
ix[]={-1,-1,0,0}
iy[]={-1,0,-1,0}
需要的状态\,/,\,/,记得转义一下。
另外注意这里的边权有0,所以一个点并不是被放入队列的时候最短,而是出队的时候最短,因为它还能被更新。这里也可以用优先队列,但是由于只有0和1两种边长,所以我们直接一种放队头一种放队尾即可,因为当前被取出来的一定是最短的,由它通过0更新的点,一定和它一样也是最短的。另外我们可以发现,能走到的点都是与起点奇偶性相同的点,因为每次移动时,横纵坐标都要发生变化。
#include<bits/stdc++.h>
using namespace std;
#define x first
#define y second
char s[600][600];
int n,m;
int d[600][600];
int dx[]={-1,-1,1,1};
int dy[]={-1,1,-1,1};
int ix[]={-1,-1,0,0};
int iy[]={-1,0,-1,0};
int st[600][600];
char op[]={'\\','/','/','\\'};
void bfs()
{memset(d,0x3f,sizeof d);memset(st,0,sizeof st);d[0][0]=0;deque<pair<int,int>>q;q.push_back({0,0});while(q.size()){auto t=q.front();q.pop_front();if(st[t.x][t.y]) continue;st[t.x][t.y]=1;if(t.x==n&&t.y==m) break;for(int i=0;i<4;i++){int nx=t.x+dx[i],ny=t.y+dy[i];if(nx<0||nx>n||ny<0||ny>m) continue;int opx=t.x+ix[i],opy=t.y+iy[i];if(s[opx][opy]==op[i]) {d[nx][ny]=min(d[nx][ny],d[t.x][t.y]);q.push_front({nx,ny});}else{d[nx][ny]=min(d[nx][ny],d[t.x][t.y]+1);q.push_back({nx,ny});}}}
}
int main()
{int t;scanf("%d",&t);while(t--){scanf("%d%d",&n,&m);for(int i=0;i<n;i++) scanf("%s",s[i]);if((n+m)%2) printf("NO SOLUTION\n"); else{bfs();cout<<d[n][m]<<endl;}}
}