BFS 全称是 \(Breadth First Search\),中文名是宽度优先搜索,也叫广度优先搜索。node
是图上最基础、最重要的搜索算法之一。ios
所谓宽度优先。就是每次都尝试访问同一层的节点。 若是同一层都访问完了,再访问下一层。算法
这样作的结果是,BFS 算法找到的路径是从起点开始的 最短 合法路径。换言之,这条路所包含的边数最小。数组
在 BFS 结束时,每一个节点都是经过从起点到该点的最短路径访问的。框架
算法过程能够看作是图上火苗传播的过程:最开始只有起点着火了,在每一时刻,有火的节点都向它相邻的全部节点传播火苗。函数
固然也能够应用于其余的一些结构中进行搜索spa
void BFS() { memset(vis,0,sizeof(vis)); queue<数据类型> q; q.push(初始值); vis[初始值]=1; while(!q.empty()) { int k=q.front(); q.pop(); //....操做,处理 if(符合条件) { q.push(当前节点); vis[当前节点]=1; } } }
根据题目要求先用函数处理好每个状态,运用\(set\)判重,由于每一层循环的操做都会是全部的操做数\(+1\),每一次弹出时判断一下是否是最终状态便可code
#include<iostream> #include<cstdio> #include<cstring> #include<queue> #include<stack> #include<map> using namespace std; const int N=10; struct node{ string xu; int num; }; string mb; string yuan="a12345678"; queue<string> q; map<string,string> vis;//判断函数,判断这一种状况有没有走过 string A(string x) { string pd=x; swap(x[1],x[8]); swap(x[2],x[7]); swap(x[3],x[6]); swap(x[4],x[5]); if(!vis.count(x))//没有这种状况没有走过,就放进去继续搜索 { vis[x]=vis[pd]+'A'; q.push(x); } return x; } string B(string x) { string pd=x; char up=x[4]; char down=x[5]; x[4]=x[3]; x[5]=x[6]; x[3]=x[2]; x[6]=x[7]; x[2]=x[1]; x[7]=x[8]; x[1]=up; x[8]=down; if(!vis.count(x)) { vis[x]=vis[pd]+'B'; q.push(x); } return x; } string C(string x) { string pd=x; char three=x[3]; char six=x[6]; char seven=x[7]; char two=x[2]; x[6]=three; x[7]=six; x[2]=seven; x[3]=two; if(!vis.count(x)) { vis[x]=vis[pd]+'C'; q.push(x); } return x; } void search() { q.push(yuan); vis[yuan]=""; while(!q.empty()) { string k=q.front(); q.pop(); A(k); B(k); C(k); if(vis.count(mb)!=0)//当出现了最终状况的时候 //这个时候由于每一次队列的头跳出能够看作可行的序列长度+1 //由于每次走的ABC三种状况是必定不一样的,因此若是出现了最终状况必定是 //第一次出现的且是操做次数最短的,因此直接输出就行。 { cout<<vis[mb].size()<<endl; cout<<vis[mb]<<endl; return; } } } int main() { mb+="a"; for(int i=1;i<=8;i++) { char a; cin>>a; mb+=a; }//将字符串的位置调到1-8便于操做 search(); return 0; }
存一下每一次走的步数,而后直接广搜就完事了,遇到最终状况就返回后输出,注意判重和打标记,由于是屡次询问,别忘了清空堆里面存的值和 \(vis\) 的值three
#include<iostream> #include<cstring> #include<cmath> #include<queue> #include<map> #include<algorithm> #include<stack> #include<cstdio> #include<vector> using namespace std; const int N=409; struct node{ int x; int y; }; queue<node> q; int dx[8]={-1,-2,-2,-1,1,2,2,1}; int dy[8]={-2,-1,1,2,2,1,-1,-2}; int vis[N][N]; int step[N][N]; int n,L; int strx,stry,endx,endy; void search() { q.push((node){strx,stry}); vis[strx][stry]=1; step[strx][stry]=0; while(!q.empty()) { node cun=q.front(); q.pop(); int x=cun.x; int y=cun.y; for(int i=0;i<8;i++) { int kx=x+dx[i]; int ky=y+dy[i]; if(kx>=0&&kx<L&&ky>=0&&ky<L&&!vis[kx][ky]) {//若是没有找过,判断是否为最终状态 if(kx==endx&&ky==endy) { cout<<step[x][y]+1<<endl; return; } else { q.push((node){kx,ky}); step[kx][ky]=step[x][y]+1; vis[kx][ky]=1; } } } } } int main() { cin>>n; while(n--) { while(!q.empty()) q.pop();//十年OI一场空,队列不空见祖宗 memset(vis,0,sizeof(vis)); //memset(step,0,sizeof(step)); cin>>L; cin>>strx>>stry; cin>>endx>>endy; if(strx==endx&&stry==endy) { cout<<0<<endl; continue; } else search(); } return 0; }
对于已经匹配好的点,直接赋值为 \(0\) 不用再去管他,对于没有匹配的点,就继续是原先的位置,再找一个数组存一下没有匹配的位置,以及与全部能够进行匹配的点之间的曼哈顿距离,而后用常规的深搜(乱入)比较一下最小值便可队列
#include<iostream> #include<cmath> #include<cstdio> #include<algorithm> #include<queue> #include<stack> #include<map> #include<vector> using namespace std; struct node { int x,y; } c[26];//未匹配点的坐标 int a[5][5]; int b[5][5]; int pipe[5][5][5][5]; //前两个下标表示未匹配点坐标 //后两个下标为待匹配点坐标 //存储未匹配点到待匹配点的最短距离 bool used[5][5]; int sum=0; int minn=0x7fffffff; void search(int x,int now) { //深搜找答案。 if(x>sum) { minn=min(minn,now); return ; } for(int i=1; i<=4; i++) for(int j=1; j<=4; j++) if(pipe[c[x].x][c[x].y][i][j]!=0&&!used[i][j])//枚举全部没有匹配的点 { used[i][j]=1; search(x+1,now+pipe[c[x].x][c[x].y][i][j]); used[i][j]=0; } } int main() { char pu; for(int i=1; i<=4; i++) for(int j=1; j<=4; j++) { cin>>pu; a[i][j]=pu-'0'; } for(int i=1; i<=4; i++) for(int j=1; j<=4; j++) { cin>>pu; b[i][j]=pu-'0'; if(a[i][j]==b[i][j]) a[i][j]=b[i][j]=0; }//若是已经匹配好了,那么就直接赋值为0,不用管了 for(int i=1; i<=4; i++) for(int j=1; j<=4; j++) if(a[i][j]==1) { c[++sum].x=i; c[sum].y=j; for(int k=1; k<=4; k++) for(int l=1; l<=4; l++) if(b[k][l]==1) { pipe[i][j][k][l]=abs(i-k)+abs(j-l); //记录一下从不匹配点到另外的不匹配点的最短距离 } } search(1,0); printf("%d",minn); return 0; }
一、最后须要你手动添加一个\(“*”\)
二、注意在其实位置按键的状况
首先咱们能够把全部相关涉及到的字符都映射为数字,方便处理,包括\((A…Z),(1…9),("*""-")\)这些东东,处理的时候别映射 \(0\) 就行
当咱们输入的时候直接映射到一个二维数组 \(a\) 来装载键盘的每一个键
将目标串映射到一个一维数组 \(b\) 中来须要按得每个键,最后别忘了手动添加一位来存 \("*"\)
那么如何选择位置呢,若是在搜索的过程当中暴力美剧会很是耗时间,那么这个时候就能够预处理每个键第一个到达的位置便可
为了方便咱们要设一个结构体类型的三维数组
结构体的变量分别为 到达的位置的横坐标 \(x\) ,到达位置的纵坐标 \(y\) ,到达这个位置的时候要匹配目标串的哪一位 \(step\) ,以及到这个点已经走了几步 \(dis\)
三维数组 \(f[k][i][j]\) 表示从 \((i,j)\)向 \(k\) 方向走到达的第一个位置的信息(即结构体中的内容)
那么预处理以后就开始广搜了
在开始广搜以前,由于咱们是从第一位开始的,那么咱们最好先预处理一下在第一位一共能够连续按几回,而后再把结构体重所表示的数据存进去,在代码中有就不详细说了
那么如今开始广搜
考虑两种状况,咱们要把选择和匹配分开找
1、
那么先是匹配,要看一下堆顶的这个位置是否和当前要匹配的位置相同,若是是相同的时候,那就在进行判断一下这个是否是最终状态,若是是,那直接把答案赋值为当前的步数+1就是最有的答案
那若是不是最后的一个状况呢
能够设一个 \(vis\) 数组,用来存当前堆顶的点接下来该匹配哪一位了,而后把相应的结构体中的数据都放进去继续查找
2、
最后再开始选择
首先枚举该点四个位置的到达点,固然由于我在预处理位置的时候有一个位置得移动忘加了,因此还要加上,一次移动
首先判断一下是否越界
而后再判断一下当前要选择的地方要比原坐标的要处理的位置要更大,那么就不须要更新 \(vis\) 数组。
不然的话,就更新当前的 \(vis\) 数组为如今要处理的位置(不用+1,由于是选择,即跳过当前的匹配直接跳进),而后把当前要选择的位置的数据放进去
最后直接输出便可
#include<iostream> #include<algorithm> #include<cstdio> #include<cmath> #include<cstring> #include<queue> #include<stack> #include<map> using namespace std; const int N=59; const int M=10009; struct node{ int x; int y; int step;//指下一个到了第几个位置, int dis;//按了几回 }f[5][N][N];//后两个表示坐标,前一个表示方向,即i,j在k方向上到的最近的点 int dx[4]={0,1,0,-1}; int dy[4]={1,0,-1,0}; char wor[M]; int n,m,len,a[N][N],b[M],vis[N][N]; map<char,int> mp; void prepare()//预先将全部的字符串处理成数字类型的,方便处理 { for(int i=0;i<=9;i++) mp[(char)('0'+i)]=i+1; for(int i=0;i<26;i++) mp[(char)('A'+i)]=i+11;//1-10被取过了 mp['-']=37; mp['*']=38; } void get()//处理一下四个方向能够到达的点 { for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) for(int k=0;k<4;k++) { int x=i; int y=j; while(a[x][y]==a[x+dx[k]][y+dy[k]])//若是一致的话 { x+=dx[k]; y+=dy[k];//一致加到不一样为止 } f[k][i][j]=(node){x,y,0,0}; } } int search() { memset(vis,0,sizeof(vis)); queue<node> q; int ans=0; int k=1; while(a[1][1]==b[k]&&k<=len) ++k;//在起点选取的状况 q.push((node){1,1,k,k-1}); vis[1][1]=k; while(!q.empty()) { node cun=q.front(); q.pop(); int x=cun.x; int y=cun.y; int now=cun.step; if(a[x][y]==b[now])//此时相等了 { if(now==len) { ans=cun.dis+1; break;//若是找完了?直接跳出 } vis[x][y]=now+1;//更新一下,由于按了一遍键盘 q.push((node){x,y,now+1,cun.dis+1}); } for(int i=0;i<4;i++) { node chose=f[i][x][y]; chose.x+=dx[i]; chose.y+=dy[i];//预处理上在加一次,保证完整 if(chose.x<1||chose.x>n||chose.y<1||chose.y>m) continue;//越界了 if(vis[chose.x][chose.y]>=now) continue;//若是选择的地方的要处理的位置比如今的要大,不用改,防止变worse vis[chose.x][chose.y]=now;//若是可行,那么把接下来要弄得位置给存进去 q.push((node){chose.x,chose.y,now,cun.dis+1});//跳到那一步 } } return ans; } int main() { prepare(); while(scanf("%d",&n)!=EOF)//UVA经典输入方式 { // cin>>n; cin>>m; for(int i=1;i<=n;i++)//输入每一行的字符串 { cin>>wor; for(int j=0;j<m;j++) a[i][j+1]=mp[wor[j]];//将每一个字符的表明的数字映射到a数组中 //从1开始方便 } cin>>wor; len=strlen(wor); for(int i=0;i<len;i++) b[i+1]=mp[wor[i]];//依旧是映射,从1开始 len++; b[len]=38;//注意最后有一个* get(); cout<<search()<<endl; } return 0; }
运用广搜板子去作便可,每一次搜索的时候都把找到的高度相同的点标记一下,避免进入死循环,一开始进入搜索的时候先把山峰山谷的值设为 \(1\),而后根据后续的判断,把不匹配的都设为 \(0\) ,最后加上便可
//#include<iostream> //#include<cstdio> //#include<cstring> //#include<algorithm> //#include<cmath> //#include<map> //#include<queue> //#include<stack> //#include<set> //#define int long long //using namespace std; //const int N=1009; //set<int> s; //int dx[8]={-1,0,1,0,-1,-1,1,1}; //int dy[8]={0,1,0,-1,-1,1,1,-1}; //int n; //int mount[N][N]; //bool vis[N][N]; //int feng; //int gu; //int big(int x,int y)//找比他小的 //{ // int ret=0; // for(int i=0;i<8;i++) // { // int kx=x+dx[i]; // int ky=y+dy[i]; // if(kx>=1&&kx<=n&&ky>=1&&ky<=n&&mount[x][y]>=mount[kx][ky]) // //若是大于周围的,不用判断周围的是否是合法,方便判断 // ret++; // } // return ret; //} //int small(int x,int y)//找比他大的 //{ // int ret=0; // for(int i=0;i<8;i++) // { // int kx=x+dx[i]; // int ky=y+dy[i]; // if(kx>=1&&kx<=n&&ky>=1&&ky<=n&&mount[x][y]<=mount[kx][ky]) // //若是小于周围的,不用判断周围的是否是合法,方便判断 // ret++; // } // return ret; //} //int cha(int x,int y){ // // int ret=0; // for(int i=0;i<8;i++) // { // int kx=x+dx[i]; // int ky=y+dy[i]; // if(kx>=1&&kx<=n&&ky>=1&&ky<=n&&mount[x][y]==mount[kx][ky]) // //若是小于周围的,不用判断周围的是否是合法,方便判断 // ret++; // } // return ret; // //} //bool check_big(int x,int y,int num,int lastx,int lasty)//检查大的 //{ // //cout<<"zsf ak ioi"<<endl; // vis[x][y]=1; // int pd=0;//用来判断符合的有多少 // int cnt=0;//用来判断一共须要符合条件的是多少 // for(int i=0;i<8;i++) // { // int kx=x+dx[i]; // int ky=y+dy[i]; // if(kx<=0||kx>n||ky<=0||ky>n) continue; // if(vis[kx][ky]) continue;//别向回找 // if(mount[kx][ky]==num)//若是是相等的值 // { // cnt++; // if(small(kx,ky)-cha(kx,ky)==0)//若是周围没有比他大的 // { // if(check_big(kx,ky,num,x,y)) pd++; // else return false; // } // else return false; // } // } // if(pd==cnt) return true; //} //bool check_small(int x,int y,int num,int lastx,int lasty)//检查小的 //{ // //cout<<"zsf ak ioi"<<endl; // vis[x][y]=1; // int pd=0;//用来判断符合的有多少 // int cnt=0;//用来判断一共须要符合条件的是多少 // for(int i=0;i<8;i++) // { // int kx=x+dx[i]; // int ky=y+dy[i]; // if(kx<=0||kx>n||ky<=0||ky>n) continue; // if(vis[kx][ky]) continue;//别向回找 // if(mount[kx][ky]==num)//若是是相等的值 // { // cnt++; // if(big(kx,ky)-cha(kx,ky)==0)//若是周围没有比他小的 // { // if(check_small(kx,ky,num,x,y))pd++; // else return false; // } // else return false; // } // } // if(pd==cnt) return true; //} //void search() //{ // for(int i=1;i<=n;i++) // for(int j=1;j<=n;j++) // { // if(vis[i][j]) continue; // int jian=cha(i,j);//减掉相等的 // int lar=big(i,j)-jian; // int simp=small(i,j)-jian; // //cout<<"i= "<<i<<" j= "<<j<<" 比他大的有"<<simp<<"个"<<" 比他小的有"<<lar<<"个"<<" 和他同样的有"<<jian<<"个"<<endl; // if(jian==8)//若是周围都是相同的,那就直接忽略掉,留到之后找 // continue; // else if(lar==0)//若是没有比他小的 // { // if(check_small(i,j,mount[i][j],i,j)) // gu++; // } // else if(simp==0)//若是没有比他大的 // { // if(check_big(i,j,mount[i][j],i,j)) // feng++; // } // } //} //signed main() //{ // cin>>n; // for(int i=1;i<=n;i++) // for(int j=1;j<=n;j++) // scanf("%lld",&mount[i][j]),s.insert(mount[i][j]); // if(s.size()==1) // { // cout<<1<<" "<<1<<endl; // return 0; // } // search(); // cout<<feng<<" "<<gu<<endl; // return 0; //} /* 暴力深搜,luogu 100pts,LOJ 62pts,好像是爆栈了=_=*/ /* 广搜大法好!!!*/ #include<cstdio> #include<iostream> #include<queue> #include<cstring> #include<cmath> #include<stack> using namespace std; const int N = 1000 + 10; int dx[8] = {-1, -1, -1, 0, 0, 1, 1, 1}; int dy[8] = {-1, 0, 1, -1, 1, -1, 0, 1}; struct node { int x, y; }; //结构体存点 int n, big, small; int map[N][N], vis[N][N]; bool sg, sf; queue<node> q; void search(int x, int y) { node start; //初始值 start.x=x, start.y=y; vis[x][y]=1; q.push(start); while (!q.empty()) { node cun=q.front(); q.pop(); //队首 for (int i=0;i<=7;i++) {//八方向 int nx=cun.x+dx[i]; int ny=cun.y+dy[i]; if (nx<1||nx>n||ny<1||ny>n) continue; //越界 if (map[nx][ny] == map[cun.x][cun.y]&&!vis[nx][ny]) { // 高度相等打上标记接着搜 vis[nx][ny] = 1; q.push((node){nx, ny}); } else { // 山峰山谷是否成立 if (map[nx][ny] > map[cun.x][cun.y]) sf = 0; if (map[nx][ny] < map[cun.x][cun.y]) sg = 0; } } } } int main() { scanf("%d",&n); bool flag=0;//判断一下是否是全部的都相等 for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) { scanf("%d", &map[i][j]); if (map[i][j]!=map[1][1]) flag=1; } if (!flag) { // 判断是否所有高度相等 cout<<1<<" "<<1<<endl; return 0; } for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) { if (!vis[i][j]) { //若是是新的联通块 sf=1,sg=1; search(i,j); big+=sf; small+=sg; } } printf("%d %d\n", big, small); return 0; }
当作一个题目的时候,尽可能先有爆搜的思路稳住分数,把暴力分拿满以后再去思考正解,稳重求进。