数据结构与算法分析(十)——母牛的故事

题目

母牛的故事
 从前有一个农夫,他的名字叫做约翰。他养了很多很多头母牛。突然有一天,一只调皮的母牛走丢了,农夫要尽快的抓住她,不然她就又跑掉了!现在我们将问题简单化。假设农夫和母牛都站在一条数轴上,农夫开始的位置为N,母牛的位置为K。
 约翰有三种行动方式,每行动一次需要一秒钟时间,假设农夫的现在的位置为X,他可以向前走一格到X+1,也可以向后走一格走到X-1,他还可以传送!一下子走到了2X

那么我们的问题是,假设母牛不会动,农夫最少需要多少秒才能抓到母牛?

输入:输入包括两个整数,用空格隔开,分别为N和K。其中0<=N,K<=100000。

输出:一个整数T,代表农夫所需的最少时间。

广度优先搜索(BFS)即可实现,这里注意每一次向外辐射得到的节点推入队列后,都要记为已访问,否则后面可能会出现覆盖的情况。

算法实现步骤:
1.每一次取出队列头部的元素x
2.分别分析2*x,x-1,x+1,如果没有访问过,则将其推入队列尾部,然后设置来源节点、距离,以及将其设置为已访问
3.判断是否与这一轮的辐射结果是否有与y相等的,有的话就跳出
注:
最后反向求解路径时,用了一个vector去反向求来源

代码实现:

/**
 *@name find_noweight:走的过程中距离不变
 *@param1 x:起始点
 *@param2 y:终止点
**/
int find_noweight(int x,int y)
{
	queue<int> q;
	int step=0;
	int first;
	q.push(x);
	visited[x]=1;	//已经访问过了x
	pre[x]=x;			//之前的元素为x
	dist[x]=0;			//该位置的元素距离为0

	while(1)
	{
		first=q.front();
		q.pop();

		if((!visited[first-1])&&(first-1>0))	//如果x-1没有被访问过 则插入
		{
			q.push(first-1);
			visited[first-1]=1;
			pre[first-1]=first;
			dist[first-1]=dist[first]+1;
			if(first-1==y)
				break;
		}
		
		if((!visited[first+1])&&(first+1<=max_num))	//如果x+1没有被访问过 则插入
		{
			q.push(first+1);
			visited[first+1]=1;
			pre[first+1]=first;
			dist[first+1]=dist[first]+1;
			if(first+1==y)
				break;
		}

		if((!visited[2*first])&&(2*first<max_num))	//如果2*x没有被访问过 则插入
		{
			q.push(2*first);
			visited[2*first]=1;	//注意这里要保证找过了就不能再找,即队列中不能有重复出现
			pre[2*first]=first;
			dist[2*first]=dist[first]+1;
			if(2*first==y)
				break;
		}
	}
	return dist[y];
	
	/*
	int temp=y;
	road.insert(road.begin(),temp);
	while(temp!=x)
	{
		temp=pre[temp];
		road.insert(road.begin(),temp);
	}*/
}

变式

农夫走到2X的地方用时2s,X+1和X-1依旧是1s

采用迪克斯特拉算法求最短路径
算法实现步骤:
1.这里建立了一个最小堆去存储进队的元素
2.取出堆的头元素x,即未访问的节点中距离最近的,然后标记为访问,再删除
3.判断取出的头元素x是否为目标值,是的话直接跳出
4.分别对2*x,x-1,x+1进行分析,如果没有访问过,且从x到该位置的距离小于原距离(如果大于的话说明到达此节点存在路径更短的路线),则将其推入堆,并对堆重新构建
注:
1.这里注意要将元素弹出推的时候才将设置为已访问,因为此时该节点的最短路径才是确定的(局部最优)

代码实现:

/**
 *@name Build_Heap:建立堆
 *@param1 i:传入需要安排的节点
**/
void Build_Heap(int i)
{
	int Child;
	int temp;
	int len=heap.size();
	//只要目标节点依旧有儿子就一直比较
	while((2*i+1)<len)
	{
		Child=2*i+1;	//左儿子的位置

		//找到最小的儿子
		if((Child+1<len)&&(dist[heap[Child]]>dist[heap[Child+1]]))	
		{
			Child++;
		}

		//判断当前节点与最小儿子大小
		//如果儿子更小的话就上浮 否则就跳出循环
		if(dist[heap[i]]>dist[heap[Child]])
		{
			swap(heap[i],heap[Child]);
			i=Child;
		}
		else
		{
			break;
		}
	}
}


/**
 *@name Dijkstra:迪克斯特拉算法
 *@param1 x:起始点
 *@param2 y:终止点
**/
void Dijkstra(int x,int y)
{
	int first;
	heap.push_back(x);
	pre[x]=x;
	dist[x]=0;

	while(1)
	{
		first=heap[0];
		visited[first]=1;					//取出堆中的首元素 并作标记已访问
		heap.erase(heap.begin());	//删除该堆的首元素

		if(first==y)
		{
			cout<<"the distance is "<<dist[first]<<endl;
			break;
		}

		if(visited[first*2]==0)		//如果2*first没有被访问过
		{
			if(dist[first*2]>dist[first]+2)	//如果从first走到2*first距离更小的话
			{
				dist[first*2]=dist[first]+2;				//更新距离 
				pre[first*2]=first;							//更新来源
				heap.insert(heap.begin(),first*2);	//并加入堆 从头部插入
				Build_Heap(0);								//重新构建堆
			}
		}

		if(visited[first+1]==0)	//如果first-1没有被访问过
		{
			if(dist[first+1]>dist[first]+1)
			{
				dist[first+1]=dist[first]+1;				//更新距离
				pre[first+1]=first;							//更新来源
				heap.insert(heap.begin(),first+1);	//加入堆 从头部插入
				Build_Heap(0);								//重新构建堆
			}
		}

		if(visited[first-1]==0)	//如果first-1没有被访问过
		{
			if(dist[first-1]>dist[first]+1)
			{
				dist[first-1]=dist[first]+1;				//更新距离
				pre[first-1]=first;								//更新来源
				heap.insert(heap.begin(),first-1);	//加入堆 从头部插入
				Build_Heap(0);								//重新构建堆
			}
		}
	}

	//寻找路径
	int temp=y;
	road.insert(road.begin(),y);
	while(temp!=x)
	{
		road.insert(road.begin(),pre[temp]);
		temp=pre[temp];
	}
}

注:
开数组的时候一定要多加10,防止越界!!!

发布了99 篇原创文章 · 获赞 29 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/weixin_44586750/article/details/103636491
今日推荐