0
点赞
收藏
分享

微信扫一扫

[Acwing提高]搜索(多源BFS-双端队列BFS)


[Acwing提高]搜索

多源BFS-双端队列BFS

知识点

题目

扩展方式

类型

矩阵距离

多源BFS

BFS最短路

魔板

扩展顺序,状态图搜索

最小步数模型

电路维修

挖掘性质对角线坐标关系

双端队列BFS

题目

矩阵距离

思路

多源BFS,求每个点(遍历到的给定点)到最近起点的距离,这里要区分多源最短路问题(任意两点间的最短距离)

求每个点到最近起点的距离,可以转为单源最短路,建立一个虚拟源点,源点和每个起点连一条边权为0的边。每个点到最近起点的最短距离可以转为每个点到虚拟源点最短距离。因为BFS会自动找距离最短分支,虚拟源点出去的每个分支就是每个起点

另外一道多源BFS:​​最优配餐​

[Acwing提高]搜索(多源BFS-双端队列BFS)_优先级

手写队列

int hh=0,tt=0;//下面有直接用了起始下标的才tt=0,实际上等效于下面的
q[0]={sx,sy};
while(hh<=tt}
{
}

int hh=0,tt=-1;
q[++tt]={sx,sy};
while(hh<=tt)
{
}

魔板

思路

最小步数模型,BFS逐渐抽象,码量逐渐暴涨,咸鱼逐渐躺下

模型抽象过程

[Acwing提高]搜索(多源BFS-双端队列BFS)_i++_02


来抽象一个BFS板子

queue<state_type>q;//队列存储状态
unordered_map<state_type,int>dist;
unordered_map<state_type,pair<op_type,state_type>>pre;
//第一个参数以第二个参数.first的方式从第二个参数.second

state_type move0(state_tpye)//转移方式
{

}
bool check(state_type tmp,state_type end)//判当前转移临时结果是否与目标态相等
{

}
op_type get_op_type(int i)//把下标转为操作序列类型
{

}
void bfs(state_type start,state_type end)
{
q.push(start);
dist[start];
while(q.size())
{
auto t=q.front();
q.pop();
state_type move_ans[N];
m[0]=move0(t);
m[1]=move1(t);
……//如果有优先级那么就按优先级映射m数组下标
m[n]=moven(t);

for(int i=0;i<N;i++)//如果有优先级那么就按优先级扩展
{
state_type tmp=m[i];
if(dist.count(tmp)==0)
{
dist[tmp]=dist[t]+1;
pre[tmp]={get_op_type(i),t};
if(check(tmp,end))break;
q.push(tmp);
}
}
}
}
int main()
{
state_type start,end;
//输入初态和目标态
bfs(start,end);
cout<<dist[end]<<endl;
opline_type res;//opline_type 用于存储操作序列的变量类型
while(!check(start,end))
{
res+=pre[end].first;
end=pre[end].second;
}
reverse(res);//想办法反转一下操作序列,如果有优先级(比如按字典序)那建议不要采取反向终点到起点的方式来实现逆序输出,老老实实反转
//输出操作序列
}

操作序列按字典序排,只要扩展的时候按字典序扩展就可以啦
应用上面板子的时候思考

state_type是什么
坐标(PII)
全局地图(string hash)
其他的hash映射搞

op_type是什么
char int

opline_type是什么
数组存,字符串,vector容器存

如何实现优先级
扩展顺序方式,贪心

如何把输入输出转化为相应的类型

代码

#include<bits/stdc++.h>
using namespace std;

int g[2][4];
unordered_map<string,int>dist;
unordered_map<string,pair<char,string>>pre;
queue<string>q;

void sett(string st)
{
for(int i=0;i<4;i++)g[0][i]=st[i];
for(int i=3,j=4;i>=0;i--,j++)g[1][i]=st[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 move0(string st)//交换上下两行
{
sett(st);
for(int i=0;i<4;i++)swap(g[0][i],g[1][i]);
return get();
}
string move1(string st)//最右一列放到最左边
{
sett(st);
char v0=g[0][3],v1=g[1][3];
for(int i=3;i>0;i--)
for(int j=0;j<2;j++)
g[j][i]=g[j][i-1];
g[0][0]=v0,g[1][0]=v1;
return get();
}
string move2(string st)//4格旋转
{
sett(st);
char 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();
}
void bfs(string st,string ed)
{
q.push(st);
dist[st]=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++)
{
string str=m[i];
if(dist.count(str)==0)
{
dist[str]=dist[t]+1;
pre[str]={char(i+'A'),t};
if(str==ed)break;
q.push(str);
}
}
}
}
int main()
{
int x;
string st,ed;
for(int i=0;i<8;i++)
{
cin>>x;
ed+=char(x+'0');
}
st="12345678";

bfs(st,ed);

cout<<dist[ed]<<endl;
string res;
while(ed!=st)
{
res+=pre[ed].first;
ed=pre[ed].second;
}
reverse(res.begin(),res.end());
if(res.size())cout<<res;
return 0;
}

电路维修

思路

挖掘性质:只能走到和是偶数的点,和是奇数的点是走不到的。所以不存在既可以主对角线又可以走副对角线的走法

[Acwing提高]搜索(多源BFS-双端队列BFS)_最短路_03


下来相当于边权0或1的无向图求最短路

双端队列,边权0入队头,边权0入对尾。只有当出队时才能确定最小值,而不是一入队就确定了,一个点会多次入队多次被扩展到的。

代码

#include<bits/stdc++.h>
#define x first
#define y second

using namespace std;
typedef pair<int,int> PII;
const int N=510,M=N*N;

int n,m;
char g[N][N];
int dist[N][N];
bool st[N][N];

int bfs()
{
deque<PII>q;
memset(dist,0x3f,sizeof dist);//每个点可能入队多次所以不要用-1了
memset(st,0,sizeof st);
char cs[5]="\\/\\/";//正确方向
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};//根据扩展的点找到对应的电路板坐标

q.push_back({0,0});
dist[0][0]=0;//不要忘记

while(q.size())
{
PII t=q.front();
q.pop_front();
int x=t.x,y=t.y;
if(x==n&&y==m)return dist[x][y];//必须出队的时候才知道最小值

if(st[x][y])continue;
st[x][y]=1;

for(int i=0;i<4;i++)
{
int a=x+dx[i],b=y+dy[i];
if(a<0||b<0||a>n||b>m)continue;
int ga=x+ix[i],gb=y+iy[i];//电路板对应坐标
int w=(g[ga][gb]!=cs[i]);//不通则边权为1,通则边权为0
int d=dist[x][y]+w;
if(d<=dist[a][b])
{
dist[a][b]=d;
if(!w)q.push_front({a,b});
else q.push_back({a,b});
}
}

}
return -1;
}

int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m);
for(int i=0;i<n;i++)scanf("%s",g[i]);
if((n+m)&1)puts("NO SOLUTION");
else printf("%d\n",bfs());

}
return 0;
}


举报

相关推荐

0 条评论