BFS基础入门
广度优先搜索是用队列来实现的,一般来解决查找最短路径的,它是从一个点 广着向外搜索,最先搜到终点的那一条路就是最短的路径了,如果题目要求输出这条路径,在搜索的过程中也可以用一个数组来记录路径。然后最后把路径输出来。刚学习BFS的时候因为还不大懂思想,似懂非懂,代码就是凭印象写的,所以经常写不出来,或者做另外一种形式的BFS题目就想不出来了,现在还算稍微理解了些,写出来加深下自己的理解。
先贴一题我第一次写的BFS吧,这题很基础,也就是一个找迷宫最短路的问题,在数据结构中也学习了图的遍历方法里面有个广度优先搜索,但是看书看的云里雾里还是多写几道题掌握的更好一些。
这道题POJ 3984 应该算是BFS中的基础入门题啦。
int maze[5][5] = {
0, 1, 0, 0, 0,
0, 1, 0, 1, 0,
0, 0, 0, 0, 0,
0, 1, 1, 1, 0,
0, 0, 0, 1, 0,
};
它表示一个迷宫,其中的1表示墙壁,0表示可以走的路,只能横着走或竖着走,不能斜着走,要求编程序找出从左上角到右下角的最短路线。
0 1 0 0 0 0 1 0 1 0 0 0 0 0 0 0 1 1 1 0 0 0 0 1 0
(0, 0) (1, 0) (2, 0) (2, 1) (2, 2) (2, 3) (2, 4) (3, 4) (4, 4)
1 #include<stdio.h> 2 #include<string.h> 3 #include<queue> 4 using namespace std; 5 int a[10][10]; 6 int vis[10][10]; 7 int mov[4][2]={ 8 1,0, 9 -1,0, 10 0,-1, 11 0,1, 12 };///代表往4个方向都可以走,上下左右 13 14 struct node///定义结构体来表示x y坐标 15 { 16 int x; 17 int y; 18 }pre[10][10],way[100]; 19 queue<node>que;///STL定义队列 20 int check(int x,int y) 21 { 22 if(x<1||x>5||y<1||y>5) 23 return 0; 24 return 1; 25 } 26 void bfs() 27 { 28 node arr; 29 arr.x=1;///由于起点是(1,1) 30 arr.y=1; 31 que.push(arr);///将第一个点放进队列 32 vis[1][1]=1; 33 while(!que.empty())///当队列不是空的时候 34 { 35 node now; 36 now=que.front(); 37 que.pop();///弹出来 38 if(now.x==5&&now.y==5)///如果广搜到(5,5)的话就return 并输出路径 39 { 40 int top=0; 41 while(1) 42 { 43 way[top++]=now;///记录路径的way数组 44 if(now.x==1&&now.y==1)///往前找 找到起点就退出 45 break; 46 now=pre[now.x][now.y]; 47 } 48 while(top--) 49 { 50 printf("(%d, %d)\n",way[top].x-1,way[top].y-1);///输出路径 51 } 52 return ; 53 } 54 for(int i=0;i<4;i++) 55 { 56 node temp;///用temp代表下一步要走的地方 57 temp.x=now.x+mov[i][0]; 58 temp.y=now.y+mov[i][1]; 59 if(check(temp.x,temp.y)==1&&vis[temp.x][temp.y]==0&&a[temp.x][temp.y]==0)///如果这个点没越界 并且没被走过 并且可以走 60 { 61 vis[temp.x][temp.y]=1;///标记这个点被走过 62 que.push(temp);///可以走这个点 入队 63 pre[temp.x][temp.y]=now;///记录路径 temp的前一个是now 64 } 65 } 66 } 67 } 68 int main() 69 { 70 for(int i=1; i<=5; i++) 71 { 72 for(int j=1; j<=5; j++) 73 { 74 scanf("%d",&a[i][j]); 75 } 76 } 77 bfs(); 78 }
这道题基本上就是广搜的思想了,把第一个点入队,然后把满足每个条件的继续入队,比如这题是满足往上下左右走的都入队,也就是继续搜索,直到队空退出或者满足了条件退出,满足的什么条件后会退出,写在if()中。
有的广搜的题目不是这种找路径的 比如其他的倒水问题之类的,做法也差不多,就是用一个for把每次可以满足的都入队,这才叫做广度搜索嘛,贴一题叫做质数大臣的题目POJ 3126
大臣们对保安局长的消息感到非常不安,因此他们不得不改变办公室的四位数房间号码。
- 时不时地改变房间号,是一个重要的安全问题。这能使得敌人无从下手。
- 但是你也知道,我选择了1033号码是有原因的。我是Prime minister(注:意译:总理,直译:质数大臣 其实是个冷笑话),你知道!
- 我知道所以你的新号码8179也是一个质数。你只需要在你办公室的门上粘贴四个新的数字。
- 不,不是那么简单。当我把第一个数字改成了8时,那么这个数字就会变成8033,这不是一个质数!
- 我知道,作为Prime minister,你无法忍受在你家门口有一个非质数,甚至几秒钟。
- 对!所以我必须发明一个从1033到8179的质数路径,从一个质数到下一个质数,每次只有一位数字能发生变化。
现在,在一旁偷听的财政部长介入了。
- 请节省没有不必要的开支!我碰巧知道改变一个数字的价格是一磅。
- 嗯,在这种情况下,我需要一个计算机程序来降低成本。你是否知道一些非常便宜的编程大师?
- 实际我已经在做了。你看,有这个编程比赛正在进行...帮助Prime minister找到任何两个给定的四位数的质数之间最便宜的质数路径!当然,第一位数字必须是非零。在上面的情况下,这里有一个解决方案。
1033
1733
3733
3739
3779
8779
8179
这个解决方案的成本是6磅。 请注意,步骤2中粘贴的数字1在最后一步不能重复使用 - 必须购买新的1。
第一行有一个正整数,代表测试数据的数量
对于接下来每一个测试数据,每行有两个数字,且两个数字都是四位质数(没有前导零)。
对于每个测试数据输出一个整数,代表最小的花费,或者输出Impossible
3 1033 8179 1373 8017 1033 1033
6 7 0
这道题目就不是找路径的但是求最少改变次数,所以也用广搜来做,每次改变4种,个十百千位都可以改变,由于一满足条件就return 所以第一个碰到满足条件的一定是最短的那一个,贴一下代码。
1 #include<vector> 2 #include<stdio.h> 3 #include<algorithm> 4 #include<math.h> 5 #include<queue> 6 #include<string.h> 7 using namespace std; 8 const int maxn=1e5+5; 9 int vis[maxn],prime[maxn]; 10 int visit[maxn]; 11 int way[maxn]; 12 int pre[maxn]; 13 int flag=0; 14 queue<int>que; 15 void is_prime() 16 { 17 int top,n; 18 top=0; 19 long long i,j; 20 for(i=2; i<maxn; i++) 21 { 22 if(vis[i]==0) 23 { 24 prime[top++]=i; 25 } 26 for(j=0; j<top&&i*prime[j]<maxn; j++) 27 { 28 vis[prime[j]*i]=1;///素数为0 29 if(i%prime[j]==0) 30 break; 31 } 32 } 33 } 34 void init() 35 { 36 memset(visit,0,sizeof(visit)); 37 memset(pre,0,sizeof(pre)); 38 memset(way,0,sizeof(way)); 39 while(!que.empty()) 40 { 41 que.pop(); 42 } 43 flag=0; 44 } 45 void bfs(int x,int y) 46 { 47 que.push(x); 48 while(!que.empty()) 49 { 50 int q=que.front();///1033 51 que.pop(); 52 if(q==y) 53 { 54 int top=0; 55 while(1) 56 { 57 way[top++]=q; 58 if(q==x) 59 break; 60 q=pre[q]; 61 } 62 /* while(top--) 63 { 64 printf("%d\n",way[top]); 65 }*/ 66 printf("%d\n",top-1); 67 flag=1; 68 return ; 69 } 70 int a,b,c,d,s,temp; 71 temp=q; 72 d=temp%10;///3 73 temp=temp/10;//103 74 c=temp%10;///3 75 temp=temp/10;//10 76 b=temp%10;///0 77 temp=temp/10;//1 78 a=temp%10;///1 79 //printf("%d %d %d %d\n",a,b,c,d); 80 for(int i=1; i<10; i+=2) ///变个位(奇数才有可能) 81 { 82 s=a*1000+b*100+c*10+i; 83 if(visit[s]==0&&s!=q&&vis[s]==0)///如果这个数字没有找过并且这个数字不是自己的话 而且是素数 84 { 85 visit[s]=1; 86 pre[s]=q; 87 que.push(s); 88 } 89 } 90 for(int i=0; i<10; i++) ///变十位 91 { 92 s=a*1000+b*100+i*10+d; 93 if(visit[s]==0&&s!=q&&vis[s]==0)///如果这个数字没有找过并且这个数字不是自己的话 而且是素数 94 { 95 visit[s]=1; 96 pre[s]=q; 97 que.push(s); 98 } 99 } 100 for(int i=0; i<10; i++) ///变百位 101 { 102 s=a*1000+i*100+c*10+d; 103 if(visit[s]==0&&s!=q&&vis[s]==0)///如果这个数字没有找过并且这个数字不是自己的话 而且是素数 104 { 105 visit[s]=1; 106 pre[s]=q; 107 que.push(s); 108 } 109 } 110 for(int i=1; i<10; i++) ///变千位(从1开始没有前导0) 111 { 112 s=i*1000+b*100+c*10+d; 113 if(visit[s]==0&&s!=q&&vis[s]==0)///如果这个数字没有找过并且这个数字不是自己的话 而且是素数 114 { 115 visit[s]=1; 116 pre[s]=q; 117 que.push(s); 118 } 119 } 120 } 121 } 122 int main() 123 { 124 int x,y,t; 125 is_prime(); 126 scanf("%d",&t); 127 while(t--) 128 { 129 init(); 130 scanf("%d %d",&x,&y); 131 bfs(x,y); 132 if(flag==0) 133 { 134 puts("Impossible"); 135 } 136 } 137 }
我目前遇到的几种关于广搜的入门问题就在这啦,其实思想就是那样的,多写写应该就会了,接下来还有搜索进阶还要学呢,不过基础打好是最重要的!