题目
母牛的故事
从前有一个农夫,他的名字叫做约翰。他养了很多很多头母牛。突然有一天,一只调皮的母牛走丢了,农夫要尽快的抓住她,不然她就又跑掉了!现在我们将问题简单化。假设农夫和母牛都站在一条数轴上,农夫开始的位置为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,防止越界!!!