广度优先遍历队列实现

本周做了两道题,Fast Robot和Picking up Jewels

先说Fast Robot,要求找出从起点到终点最少拐弯次数。

这道题其实有一种很简单的算法:

1.      从起点开始,将拐1次弯的点全部入队,map[i][j].path全部标记成1(并且标记为已访问),然后起点出队。

2.      拐一次弯的第一个点出队,将第一个点作为起点,所有拐一次弯并且未入队的点入队,即总计拐两次弯的点入队。以此类推,直到将所有的拐一次弯的点全部出队。

3.      同理所有的可以走到的点都会入队出队,直到找到终点。

在此原理的基础上,只要找到了终点,那就是最短的拐弯次数。

 

         可是做的时候并没有去思考过多,上来就用了特别朴素的方法,也特别复杂。换了好几次尝试,从不用指针实现的队到用指针实现的队,最后又换回了不用指针实现的队。

首先 

int DIR[4][2] ={{-1,0},{0,1},{1,0},{0,-1}};   //正点方向顺时针 上右下左

void BFS(Node start, Node end) {
	Node t1;
	enqueue(start); // 将起始点进队
	int newValue = 0;
	int i;
	int dx = 0, dy = 0;

	while(!isEmpty()) {  // 如果队不为空
		t1 = dequeue();  // 出队
		for(i=0; i<4; i++)  //扫描当前节点的4个方向
		{
			dx = t1.x+DIR[i][0];
			dy = t1.y+DIR[i][1];
			if(dx == end.x && dy == end.y)
			{
				if(t1.dir != i)   //如果当前节点方向与即将要前进的方向(到终点)不一致,则拐弯数+1
				{
					Answer = Answer<t1.value+1?Answer:t1.value+1;  //比较新的路径的拐弯数和当前所记录的最小值,取小的数
				} else {
					Answer  = Answer<t1.value?Answer:t1.value;
				}
#ifdef TEST
				cout<<"find end!:"<<dx<<","<<dy<<", Answer = "<<Answer<<endl;
				printOMap();
#endif
				continue;  // 终点不进队,所以用continue
			}
			if(dx>=0 && dy>=0 && dx<N && dy<M && map[dx][dy].d=='0')  // '0'表示可走的路
			{
				if(t1.dir != i)           //如果当前节点方向与即将要前进的方向不一致,则拐弯数+1
				{
					newValue = t1.value+1;
				}else {
					newValue = t1.value;
				}
				if(map[dx][dy].dir == -1 || newValue<=map[dx][dy].value) {   //如果 要判断的点是从没访问过的点  或者  已经访问过但是新的路径拐弯次数少于之前保存的拐弯次数   
					map[dx][dy].dir = i;   // 将所判断的点的方向置为当前行进方向
					map[dx][dy].value = newValue;   // 将所判断的点的拐弯书置为新的路径拐弯次数
					enqueue(map[dx][dy]);   // 进队  这里本来是要用指针来实现的,但是因为本题设计到拐弯数和方向两个变量调控的原因,
										    // 只保存一个值的话会有问题。除非添加多个方向的变量,否则最好是使用拷贝的方式进队
				}
			}
		}
	}
}

我是那种建模想象能力不太好的人,一般都需要画出图来理解,所以我test的方式是把整个map当前的状态绘制了出来。

void printOMap()
{
	cout<<"---------------------N="<<N<<",M="<<M<<endl;
	int i,j;
	for(i=0; i<N; i++)
	{
		for(j=0; j<M; j++)
		{
			if(map[i][j].value == BMAX)
			{	cout<<"*"<<" ";
			}else{
		        cout<<map[i][j].value<<" ";
			}
		}
		cout<<endl;
	}
}


7 7   // 7列7行

1 2 7 5  // start end

1111111  // 1是墙,0是路

0000011

1011001

1011100

1011110

1000000

1111111

find end!:4,6, Answer = 5

---------------------N=7,M=7

* * * * * * *

1 0 0 0 0 * *

* 1 * * 1 2 *

* 1 * * * 3 4

* 1 * * * * -1

* 1 2 2 2 * *

* * * * * * *

find end!:4,6, Answer = 3

---------------------N=7,M=7

* * * * * * *

1 0 0 0 0 * *

* 1 * * 1 2 *

* 1 * * * 3 4

* 1 * * * * -1

* 1 2 2 2 2 2

* * * * * * *

因此最小的拐弯数就是3了。

       在做这道题的时候,我曾遇到一个百思难得其解的问题。就是小的数据运算结果都是OK的,但是一碰到大的数据的时候,DFS后的数组输出就有问题了。



100x120的数据为什么输出变成了7行1列呢。研究了半天没整明白。最后在老大的帮助下,发现原来我的循环队列写成了顺序队列,40000的长度都没打住,把N和M的空间给踩了,可见这复杂的。也由此暴露出来了一个问题,这基础多不牢固能犯这错误,哈哈。

现在来说说队列怎么写吧。

typedef struct {
	int x;
	int y;
	int dir;
	int value;
	char d;
}Node;  // 节点结构体
Node map[MAX][MAX];  // 节点数组用于存储map

typedef struct {
	Node data[LENGTH];
	int rear;
	int front;
}Queue;  // 队列结构体
Queue que;   // 队列

Node map[MAX][MAX];  输入是char型的字符,那么怎么存储成Node的数组呢

如果是char map[MAX][MAX],那么你可以这样

for(i=0; i<N; i++) {

cin>>map[i];

}

然而Node型的你则需要逐个地输入了。

for(i=0; i<N; i++)
{
    for(j=0; j<M; j++)
    {
        cin>>map[i][j].d;
        map[i][j].x = i;
        map[i][j].y = j;
        map[i][j].dir = -1;
        map[i][j].value = 999999;
    }
}

队列一般需要队空、队满、入队、出队、获取对头、初始化等方法。

bool isEmpty()
{
    if(que.rear == que.front){
        return true;
    }else {
        return false;
    }
}

bool isFull()
{
    if((que.rear+1)%LENGTH == que.front){
        return true;
    }else {
        return false;
    }
}

void enqueue(Node n)
{
#ifdef QLOG
    cout<<"enqueue:"<<n.x<<","<<n.y<<",dir = "<<n.dir<<",value = "<<n.value<<endl;
#endif
    if(!isFull())
    {
        que.data[que.rear] = n;
        que.rear = (que.rear+1)%LENGTH;
        // que.rear++;   // 顺序队列
    } else {    
#ifdef QLOG
    cout<<"enqueue fail because queue is full"<<endl;
#endif
    }
    
}

Node dequeue()
{
    Node n = que.data[que.front];
    if(!isEmpty())
    {
        que.front = (que.front+1)%LENGTH;
        //que.front++;  // 顺序队列
#ifdef QLOG
        cout<<"dequeue:"<<n.x<<","<<n.y<<",dir = "<<n.dir<<",n.value = "<<n.value<<endl;
#endif
    } else {
#ifdef QLOG
        cout<<"Queue is empty"<<endl;
#endif
    }
    return n;
}

Node getTop()
{
    Node n = que.data[que.front];
#ifdef QLOG
    cout<<"getTop:"<<n.x<<","<<n.y<<",dir = "<<n.dir<<endl;
#endif
    return n;
}

void initQueue()
{
    que.front = que.rear = 0;
}

到此基本上这个问题就说完了。





  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值