1.记录位置信息:https://www.acwing.com/problem/content/1078/
AC代码:
#include<bits/stdc++.h>
using namespace std;
#define x first
#define y second
queue<pair<int,int> > q;
int n;
int a[1010][1010];
pair<int,int>pre[1010][1010];
bool vis[1010][1010];
int dx[4]={1,0,-1,0};
int dy[4]={0,1,0,-1};
void bfs(){
vis[n][n]=1;
pre[n][n]={-1,-1};
q.push({n,n});
while(!q.empty()){
pair<int,int> ck=q.front();
q.pop();
if(ck.x==1&&ck.y==1) break;
for(int i=0;i<4;i++){
int sx=ck.x+dx[i],sy=ck.y+dy[i];
if(a[sx][sy]) continue;
if(sx<1||sx>n||sy<1||sy>n) continue;
if(vis[sx][sy]) continue;
vis[sx][sy]=1;
pre[sx][sy].x=ck.x;
pre[sx][sy].y=ck.y;
q.push({sx,sy});
}
}
}
int main(){
cin>>n;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
cin>>a[i][j];
}
}
//从后往前搜
bfs();
int i=1,j=1;
cout<<i-1<<" "<<j-1<<endl;
//cout<<pre[n-1][n].x<<" "<<pre[n-1][n].y<<"gkggkkg";
//cout<<pre[3][5].x<<"gfdf"<<pre[3][5].y<<"dfsdf";
while(i!=n||j!=n){
cout<<pre[i][j].x-1<<" "<<pre[i][j].y-1<<endl;
int i1=pre[i][j].x,j1=pre[i][j].y;
i=i1;
j=j1;
}
//cout<<n-1<<" "<<n-1;
}
2.最小步数模型:https://www.acwing.com/problem/content/1109/
这里注意两种形态上的转化:
AC代码:
#include<bits/stdc++.h>
using namespace std;
map<string,int> dist;
map<string,pair<char,string> > pre;
queue<string> q;
char g[2][4];
void set1(string ss){
for(int i=0;i<4;i++) g[0][i]=ss[i];
for(int i=3,j=4;i>=0;j++,i--) g[1][i]=ss[j];
}
string get(){
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 mo1(string ss){
set1(ss);
for(int i=0;i<4;i++) swap(g[0][i],g[1][i]);
return get();
}
string mo2(string state)
{
set1(state);
int v0 = g[0][3], v1 = 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] = v0, g[1][0] = v1;
return get();
}
string mo3(string state)
{
set1(state);
int v = g[0][1];
g[0][1] = g[1][1];
g[1][1] = g[1][2];
g[1][2] = g[0][2];
g[0][2] = v;
return get();
}
int bfs(string start,string end){
if(start==end) return 0;
q.push(start);
dist[start]=0;
while(!q.empty()){
auto t=q.front();
q.pop();
string m[3];
m[0]=mo1(t);
m[1]=mo2(t);
m[2]=mo3(t);
for(int i=0;i<3;i++){
string k=m[i];
if(dist.count(k)==0){
dist[k]=dist[t]+1;
pre[k]={char(i+'A'),t};
if(k==end) return dist[end];
q.push(k);
}
}
}
return -1;
}
int main(){
int x;
string start,end;
for(int i=0;i<8;i++){
cin>>x;
end+=char(x+'0');
}
for(int i=0;i<8;i++) start+=char(i+'1');
int ck=bfs(start,end);
cout<<ck<<endl;
string res;
while(end!=start){
res+=pre[end].first;
end=pre[end].second;
}
reverse(res.begin(), res.end());
if (ck > 0) cout << res << endl;
}
3.双端队列广搜:https://www.acwing.com/problem/content/177/
这一题主要考的就是双端队列,但是我觉得这一题还考察了具体在点和块线上的位置移动实现过程。
我们要知道点与格子是两码事:
假如一个点位于(1,1),那么它可以往左上脚走(-1,-1),而他经过的格子是(0,0)。
因此我们在写点走的方向时也应对应好走过的格子的方向。
具体见代码:
#include<bits/stdc++.h>
#define rep(i, a, b) for(int i = a; i < b; i++)
#define x first
#define y second
using namespace std;
typedef pair<int, int> PII;
const int N = 510;
int dir[4][2] = {{-1, -1}, {-1, 1}, {1, 1}, {1, -1}}; //4个方向移动后点的坐标
int diri[4][2] = {{-1, -1}, {-1, 0}, {0, 0}, {0, -1}}; //移动后方格的坐标(左上角代表方格)
char cs[5] = {'\\', '/', '\\', '/'};
char g[N][N];
int dis[N][N];
bool st[N][N];
int n, m, T;
int bfs()
{
memset(dis, 0x3f, sizeof dis);
memset(st, 0, sizeof st);
dis[0][0] = 0;
deque<PII> q;
q.push_back({0, 0});
while(q.size())
{
auto t = q.front();
q.pop_front();
if(t.x == n && t.y == m) //双端队列bfs第一次遇到一定是 最短距离
break;
if(st[t.x][t.y])
continue;
st[t.x][t.y] = 1;
rep(i, 0, 4)
{
int a = t.x + dir[i][0], b = t.y + dir[i][1];//下一个点的坐标
if(a < 0 || a > n || b < 0 || b > m) continue;
int ca = t.x + diri[i][0], cb = t.y + diri[i][1]; //下个方格对应的坐标
int d = dis[t.x][t.y] + (cs[i] != g[ca][cb]); //当前的节点到相邻节点的距离
//如果可以更新最短距离
if(d < dis[a][b]) //注意,我们需要用点的坐标,方格坐标只是用来判断 “边权” 的
{
dis[a][b] = d;
//注意我们的研究对象,是 点 的坐标的奥
if(cs[i] == g[ca][cb]) //距离为0,那么下一次继续遍历这个点,实现了贪心的“最短”
q.push_front({a, b});
else
q.push_back({a, b});
}
}
}
return dis[n][m]; //返回到右下角最短距离
}
int main()
{
cin >> T;
while(T -- )
{
cin >> n >> m; //x、y之和为奇数的点必然不能到达
rep(i, 0, n) scanf("%s", &g[i]);
int t = bfs();
if(t == 0x3f3f3f3f)
puts("NO SOLUTION");
else
printf("%d\n", t);
}
}
4.双向BFS:https://www.acwing.com/problem/content/192/
注意一下几个点:
1.假如一个队列空了,那么从头到结尾与从结尾到头不连通。
2.每一次BFS扩展的时候总是挑元素少的队列,这样效率更高。
AC代码:
#include<bits/stdc++.h>
using namespace std;
string A,B;
string a[100],b[100];
int k=1;
map<string,int> dis1;
map<string,int> dis2;
queue<string> qa;
queue<string> qb;
int ext(queue<string>& qa,map<string,int>& dis1,map<string,int>& dis2,string a[],string b[]){
int ck=dis1[qa.front()];
while(!qa.empty()&&dis1[qa.front()]==ck){
auto t=qa.front();
qa.pop();
for(int i=1;i<=k;i++){
for(int j=0;j<t.size();j++){
if(a[i]!=t.substr(j,a[i].size())) continue;
string hh=t.substr(0,j)+b[i]+t.substr(j+a[i].size());
if(dis1.count(hh)!=0) continue;
dis1[hh]=dis1[t]+1;
if(dis2.count(hh)!=0){
return dis1[hh]+dis2[hh];
}
qa.push(hh);
}
}
}
return 11;
}
int bfs(){
if (A == B) return 0;
qa.push(A);
qb.push(B);
dis1[A]=0,dis2[B]=0;
int step=0;
while(!qa.empty()&&!qb.empty()){
int t;
if(qa.size()<=qb.size()) t=ext(qa,dis1,dis2,a,b);
else t=ext(qb,dis2,dis1,b,a);
if(t<=10) return step+1;
step++;
if(step==10) return 11;
}
return 11;
}
int main(){
cin>>A>>B;
while(cin>>a[k]>>b[k]) k++;
int t=bfs();
if(t>10) cout<<"NO ANSWER!"<<endl;
else cout<<t;
}