多源BFS、最小步数模型、双端队列广搜
#写在前面
无
#多源BFS
##矩阵距离
https://www.acwing.com/problem/content/175/
曼哈顿距离:行和列距离之和
----c++版
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
#define x first
#define y second
//一个朴素的想法是分别以每个1为起点,遍历矩阵,每遍历一次有一个最小值矩阵
//再选出每个点对于每个起点最小距离中的最小值
//其实把每个起点看成第二层的点就好了
//建一个虚拟的起点,到每个题目中起点的距离为0
//以这个点开始就只用一次bfs了
const int N=1011;
typedef pair<int, int> pll;
int n,m;
char g[N][N];
pll q[N*N];
int dist[N][N];
void bfs(){
memset(dist, -1, sizeof dist);
int hh=0, tt=-1;
for(int i=0; i<n; i++)
for(int j=0; j<m; j++)
if(g[i][j]=='1'){
dist[i][j]=0;
q[++tt]={i, j};
}
int dx[4]={-1, 0, 1, 0}, dy[4]={0, 1, 0, -1};
while(hh<=tt){
auto t=q[hh++];
for(int i=0; i<4; i++){
int a=t.x+dx[i], b=t.y+dy[i];
if(a<0||a>=n||b<0||b>=m)continue;
if(dist[a][b]!=-1)continue;
dist[a][b]=dist[t.x][t.y]+1;
q[++tt]={a, b};
}
}
}
int main(){
scanf("%d%d", &n, &m);
for(int i=0; i<n; i++)scanf("%s", g[i]);
bfs();
for(int i=0; i<n; i++){
for(int j=0; j<m; j++)
printf("%d ", dist[i][j]);
printf("\n");
}
return 0;
}
#最小步数模型
一个棋盘变成另一个棋盘
看看需要变多少次
存每一个棋盘的状态,一般需要一个哈希函数
##八数码
https://www.acwing.com/problem/content/847/
----c++版
//状态表示
//状态转移
//路径存储
#include<iostream>
#include<algorithm>
#include<unordered_map>
#include<queue>
#include<cstring>
using namespace std;
int bfs(string start){
string end = "12345678x";//终点
queue<string>q;
unordered_map<string, int>d;//距离数组
q.push(start);
d[start]=0;//unordered_map的用法
int dx[4]={-1,0,1,0},dy[4]={0,1,0,-1};//对称写法
while(q.size()){
auto t=q.front();
q.pop();
int distance=d[t];//distance为当前节点的步数
if(t==end)return distance;
//状态转移
int k=t.find('x');
int x=k/3,y=k%3;//小技巧,一维数组下标转换为二维数组下标
for(int i=0;i<4;i++){
int a=x+dx[i],b=y+dy[i];
if(a>=0&&a<3&&b>=0&&b<3){
swap(t[k], t[a*3+b]);//状态转移,获得新状态
//走过的一定是最短的
if(!d.count(t)){//如果没走过
d[t]=distance+1;//加上距离
q.push(t);//加入待拓展队列
}
swap(t[k], t[a*3+b]);
}
}
}
return -1;
}
int main(){
string state;
for(int i=0;i<9;i++){
char c;
cin>>c;
state+=c;
}
cout<<bfs(state)<<endl;
return 0;
}
##魔板
https://www.acwing.com/problem/content/1109/
这题可以用康托展开,把一个状态映射到一个数上
c++可以用map
c++11可以用unordered_map
----c++版
#include<iostream>
#include<algorithm>
#include<cstring>
#include<unordered_map>
#include<queue>
using namespace std;
//在扩展每一个状态的时候,只要按照ABC的顺序去搜,就一定可以得到最小字典序
//队列每一层都会按照ABC排列,
char g[2][4];
unordered_map<string, int> dist;
unordered_map<string, pair<char, string>> pre;
queue<string> q;
void set(string state){
for(int i=0; i<4; i++)g[0][i]=state[i];
for(int i=3,j=4; i>=0; i--,j++)g[1][i]=state[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 state){
set(state);
for(int i=0; i<4; i++) swap(g[0][i], g[1][i]);
return get();
}
string move1(string state){
set(state);
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 state){
set(state);
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 start, string end){
if(start==end)return;
q.push(start);
dist[start]=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==end)break;
q.push(str);
}
}
}
}
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');
bfs(start, end);
cout<<dist[end]<<endl;
string res;
while(end!=start){
res+=pre[end].first;
end=pre[end].second;
}
reverse(res.begin(), res.end());
if(res.size())cout<<res<<endl;
return 0;
}
#双端队列广搜
##电路维修
https://www.acwing.com/problem/content/177/
----c++版
#include<iostream>
#include<algorithm>
#include<cstring>
#include<deque>//双端队列
using namespace std;
//dijkstra只要边权不小于0都是可以做的
#define x first
#define y second
typedef pair<int, int> pll;
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<pll> q;
memset(st, 0, sizeof st);
memset(dist, 0x3f, sizeof dist);
dist[0][0]=0;
int dx[4]={-1, -1, 1, 1};
int dy[4]={-1, 1, 1, -1};//点的走法
char cs[]="\\/\\/";
int ix[4]={-1, -1, 0, 0};
int iy[4]={-1, 0, 0, -1};//格子的走法
q.push_back({0,0});
while(q.size()){
auto 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]=true;
for(int i=0; i<4; i++){
int a=x+dx[i], b=y+dy[i];
if(a<0||a>n||b<0||b>m)continue;//格点比格子多一
int ga=x+ix[i], gb=y+iy[i];
int w=(g[ga][gb]!=cs[i]);
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;
cin>>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;
}