当青训营遇上码上掘金
题目
主题 3:寻友之旅
小青要找小码去玩,他们的家在一条直线上,当前小青在地点 N ,小码在地点 K (0≤N , K≤100 000),并且小码在自己家原地不动等待小青。小青有两种交通方式可选:步行和公交。
步行:小青可以在一分钟内从任意节点 X 移动到节点 X-1 或 X+1
公交:小青可以在一分钟内从任意节点 X 移动到节点 2×X (公交不可以向后走)
请帮助小青通知小码,小青最快到达时间是多久?
输入: 两个整数 N 和 K
输出: 小青到小码家所需的最短时间(以分钟为单位)
作者:青训营官方账号
链接:juejin.cn/post/718775…
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
解法
Dijkstra
可将整条直线抽象为有向无环图,对于图上的节点X,存在X-1、X+1、X*2这三个临接节点,距离为1。
对于起始点,距离最近且未访问过的顶点即为它的所有邻接节点(距离都为1),第1次,我们遍历它们,第2次我们依次遍历它们未访问过的临接节点,再将这些临接节点的临接节点加入下一次遍历的节点中,对于第i次要遍历的节点,距离为i,也是距离最近且未访问过的顶点(小于i的节点都已遍历过),直到扩展到终点为止,此时到达终点的距离即为最小距离。
定义节点:
class node_dst
{
public:
int node;
int dst;
node_dst(int a, int b)
{
node = a;
dst = b;
}
void print()
{
cout << "node: " << node << "\tdst: " << dst << endl;
}
};
算法:
这里用到集合来存储已经遍历过的节点
这里用到队列,从队头取出当前遍历的节点,再将该节点的临接节点加入到队列末尾。 添加之前需要对范围做校验,还要校验其不在集合内(未遍历过)
int findFriend(int n, int k)
{
if (n < 0 || k > 10e5)
{
cout << "invalid input!" << endl;
return -1;
}
if (n >= k)
{
return n - k;
}
queue<node_dst> q;
node_dst start(n, 0);
set<int> has;
int ans;
q.push(start);
has.insert(n);
while (!q.empty())
{
node_dst recent = q.front();
recent.print();
if (recent.node == k)
{
ans = recent.dst;
break;
}
q.pop();
if (recent.node + 1 <= 10e5 && has.find(recent.node + 1) == has.end())
{
has.insert(recent.node + 1);
node_dst temp(recent.node + 1, recent.dst + 1);
q.push(temp);
}
if (recent.node - 1 < 0 && has.find(recent.node - 1) == has.end())
{
has.insert(recent.node - 1);
node_dst temp(recent.node - 1, recent.dst + 1);
q.push(temp);
}
if (recent.node * 2 <= 10e5 && has.find(recent.node * 2) == has.end())
{
has.insert(recent.node * 2);
node_dst temp(recent.node * 2, recent.dst + 1);
q.push(temp);
}
}
return ans;
}