经典BFS以及奇特的做法。。

概念:宽度优先搜索(又称广度优先搜索)是最简便的图的搜索算法之一,这一算法也是很多重要的图的算法的原型。Dijkstra算法和Prim算法都采用了和宽度优先搜索类似的思想。BFS属于一种盲目搜寻法(所以才叫搜索啊),用队列实现,目的是系统地展开并检查图中的所有节点,以找寻结果。(不要怪我扯上图论)换句话说,它并不考虑结果的可能位置,彻底地搜索整张图,直到找到结果为止。

相信对BFS大家都不陌生,下面直接看题:

                          T1.

有一种很奇怪的电梯。大楼的每一层楼都可以停电梯,而且第i层楼(1<=i<=N)上有一个数字Ki(0<=Ki<=N)(N<100)。电梯只有四个按钮:开,关,上,下。上下的层数等于当前楼层上的那个数字。当然,如果不能满足要求,相应的按钮就会失灵。例如:3 3 1 2 5代表了Ki(K1=3,K2=3,……),从一楼开始。在一楼,按可以到4楼,按是不起作用的,因为没有-2楼。那么,从A楼到B楼至少要按几次按钮呢?若无法到达就输出-1

样例输入:
    5 1 5

3 3 1 2 5

样例输出:

   3

这是一道很经典的题目,但为什么要用BFS做呢?(当然IDA*也是可以的)

 思考:如果用DFS,可能会发生什么?

 极有可能永远搜不到结果导致TLERE

 至于实现就很简单了:

#include<bits/stdc++.h>
using namespace std;
int n,m=-1,k,st,en,tmp,a[201]={},c[201]={};
bool b[201]={};
queue<int>q;
int bfs(){
	tmp=st;b[tmp]=1;c[tmp]=0;
	q.push(tmp);
	do{
		k=q.front();q.pop();
		if((k-a[k]>0)&&(!b[k-a[k]])){
			b[k-a[k]]=1;
			q.push(k-a[k]);
			c[k-a[k]]=c[k]+1;
		}
		if((k+a[k]<=n)&&(!b[k+a[k]])){
			b[k+a[k]]=1;
			q.push(k+a[k]);
			c[k+a[k]]=c[k]+1;
		}
		if(b[en])return c[en];
	}while(!q.empty());
	return -1;
}
int main(){
	memset(a,0,sizeof(a));
	memset(b,0,sizeof(b));
	memset(c,0,sizeof(c));
	cin>>n>>st>>en;
	for(int i=1;i<=n;i++)cin>>a[i];
	m=bfs();cout<<m;
	return 0;
}

    是不是觉得太简单了,还有一题:

                    T2.

   Farmer John为了满足宁智贤对美的享受而安装了人工湖。矩形的人工湖分成MN(1 <= M<= 30; 1 <= N <= 30)的方形小格子。有些格子有美丽的荷叶,有些有岩石,剩下的格子有的只是美丽的蓝色湖水。宁智贤通过从一片荷叶跳到另一片荷叶上来练习芭蕾。它现在正站在一片荷叶上(看输入数据了解具体位置)。它希望通过在荷叶上跳跃来到达另一片荷叶。它既不能跳到水里也不能跳到岩石上。只有新手才会感到吃惊:宁智贤的跳跃有点类似国际象棋中马那样的移动,在一个方向上(如水平方向)移动M1(1 <=M1 <= 30)“,然后再在另一个方向上(如竖直方向)移动M2 (1 <= M2 <=30; M1 != M2)格,所以宁智贤有时可能有多达8中的跳跃选择。
给出池塘的构造以及宁智贤跳跃的形式,找出宁智贤从一个位置移动到另一个位置所需的最小的跳跃次数。(保证有解)

输入格式:

i+1行用N个空格分开的整数描述池塘第i行,0表示水,1表示荷叶,2表示岩石,3表示宁智贤现在站的那块荷叶,4表示跳跃的终点。

样例输入:

4 5 1 2

1 0 1 0 1

3 0 2 0 4

0 1 2 0 0

0 0 0 1 0

样例输出:
2

  这道题又为什么要用BFS做呢?

  当数据较小时,用DFS完全没有问题,但是数据如果较大,因为每一层会有8种情况,会TLE。同时,也极有可能出现找不到解的情况,所以只能用BFS解决。(IDA*。。应该还是可以的)。

  思路确定,实现还是简单的:

 
  
#include<bits/stdc++.h> 
using namespace std;  
const int maxn=40;  
struct node{  
   int x,y,step;  //分别为行、列、步数 
}start,end,temp,tmp;  
int n,m,ans;  
bool ma[maxn][maxn],vis[maxn][maxn]={0};  
int dx[8],dy[8];  
int bfs()  
{  
    queue<node>q;  //队列,应该知道吧! 
    temp.x=start.x;temp.y=start.y;temp.step=0;  
    vis[temp.x][temp.y]=1;  
    q.push(temp);  
    while(!q.empty())  //如果队不为空 
    {  
        temp=q.front();  //队首 
        q.pop();  
        for(int i=0;i<8;i++)  //八个方向逐个寻找 
        {  
            tmp=temp;  
            tmp.step++;tmp.x+=dx[i];tmp.y+=dy[i];  
            if(tmp.x==end.x && tmp.y==end.y)   //如果到终点 
                return tmp.step;  
            if((tmp.x<1)||(tmp.y<1)||(tmp.x>n)||(tmp.y>m)||(ma[tmp.x][tmp.y]==1)||(vis[tmp.x][tmp.y]==1))   
               continue;   
            else {  
              vis[tmp.x][tmp.y]=1;q.push(tmp);  //标记,入队 
            }  
        }  
    }  
}  
int main()  
{  
    int m1,m2,a;  
    cin>>n>>m>>m1>>m2;
    dx[0]=-m1;dy[0]=m2; dx[1]=-m1;dy[1]=-m2;  
    dx[2]=-m2;dy[2]=-m1;dx[3]=-m2;dy[3]=m1;  
	dx[4]=m1;dy[4]=m2;  dx[5]=m1;dy[5]=-m2;  
    dx[6]=m2;dy[6]=m1;  dx[7]=m2;dy[7]=-m1;  //方向数组赋值 
    for(int i=1;i<=n;i++)  
        for(int j=1;j<=m;j++){  
            cin>>a;  
            if(a==0 || a==2) ma[i][j]=1;  //1为水或岩石 
             else ma[i][j]=0;  
            if(a==3){  
              start.x=i;start.y=j;  
            }  
            if(a==4){  
              end.x=i;end.y=j;  
            }  
        }  
    ans=bfs();  
    cout<<ans<<endl;  
    return 0;  
}

———————————————————正经分割线————————————————————

上面给出了两道例题,先看第一道:

  观察数据范围:N<100。

  再思考题目:对于一个点i,可以去到i-ki和i+ki这两个点(如果这两个点都在范围内的话)。

把这些点转化到图上,即第i个点与i-ki和i+ki连一条权值为1的有向边,那么就把问题转化成由点a去到点b的最短路了。

因为数据量小,用floyd就可以轻松解决:

 
  
#include<bits/stdc++.h>
#pragma G++ optimize(2)
using namespace std;
int f[500][500];
int n,k,m;
int main()
{
	memset(f,10,sizeof(f));
	scanf("%d%d%d",&n,&k,&m);
	for(int i=1;i<=n;i++)
	 {
	 	int x;
	 	scanf("%d",&x);
	 	if (i-x>=1) f[i][i-x]=1;
		if (i+x<=n) f[i][i+x]=1;
		f[i][i]=0; 
	 }
	for(int k=1;k<=n;k++) 
	 for(int i=1;i<=n;i++)
	  for(int j=1;j<=n;j++)
	   if (f[i][k]+f[k][j]<f[i][j]) f[i][j]=f[i][k]+f[k][j];
	if (f[k][m]<=10000000) printf("%d",f[k][m]);
	 else printf("-1");
	return 0; 
}

是不是很惊讶,再观察T2:

  数据范围:1≤m≤30,1≤n≤30,即最多有900个点

  对于每个点,可能有八条连边(注意在这里连的是无向边,权值仍为1,利用这个可以有一些小优化)

仔细分析后,可以发现,用if和continue优化常数,floyed依然可以轻松过这道题,下面看代码:

 
  
#include<bits/stdc++.h>
#pragma G++ optimize(2)
using namespace std;
int f[1000][1000],b[100][100]={},a[100][100]={};
int n,m,l,r,x,y,xx,yy;
int main()
{
	memset(f,10,sizeof(f));
	scanf("%d%d%d%d",&n,&m,&l,&r);
	for(int i=1;i<=n;i++)
	 {
	 	for(int j=1;j<=m;j++)
	 	 {
	 	 	b[i][j]=(i-1)*m+j;
		  }
	 }
	for(int i=1;i<=n*m;i++)
	 f[i][i]=0; 
	for(int i=1;i<=n;i++)
	 {
	 	for(int j=1;j<=m;j++)
	 	 {
            scanf("%d",&a[i][j]);
            if (a[i][j]==4)
             {
             	x=i;
             	y=j;
             	continue;
             }
            if (a[i][j]==3)
             {
             	xx=i;
             	yy=j;
             	continue;
             }
            if (a[i][j]==2)
			 a[i][j]=0; 
	 	 }
	 }
	for(int i=1;i<=n;i++)
	 {
	 	for(int j=1;j<=m;j++)
	 	 {
	 	 	if (a[i][j]==0) continue;
	 	 	if (i+r<=n&&j-l>=1) 
	 	 	if (a[i+r][j-l]!=0) f[b[i][j]][b[i+r][j-l]]=1,f[b[i+r][j-l]][b[i][j]]=1;
	 	 	if (i+r<=n&&j+l<=m) 
	 	 	if (a[i+r][j+l]!=0) f[b[i][j]][b[i+r][j+l]]=1,f[b[i+r][j+l]][b[i][j]]=1;
	 	 	
	 	 	if (i+l<=n&&j-r>=1) 
	 	 	if (a[i+l][j-r]!=0) f[b[i][j]][b[i+l][j-r]]=1,f[b[i+l][j-r]][b[i][j]]=1;
	 	 	if (i+l<=n&&j+r<=m) 
	 	 	if (a[i+l][j+r]!=0) f[b[i][j]][b[i+l][j+r]]=1,f[b[i+l][j+r]][b[i][j]]=1;
	 	 }
	 }
	for(int k=1;k<=n*m;k++) 
	 for(int i=1;i<=n*m;i++)
	  for(int j=1;j<=n*m;j++)
	   if (f[i][k]+f[k][j]<f[i][j]) 
	    {
		 f[i][j]=f[i][k]+f[k][j];
		} 
	if (f[(xx-1)*m+yy][(x-1)*m+y]<168430090) printf("%d\n",f[(xx-1)*m+yy][(x-1)*m+y]);
	return 0; 
}

是不是很有趣呢

————————————————————完——————————————————————

猜你喜欢

转载自blog.csdn.net/qq_40716114/article/details/80449268