有向树的叶到根的最短距离
Posted
技术标签:
【中文标题】有向树的叶到根的最短距离【英文标题】:Shortest Distance from Leaf to Root of a Directed tree 【发布时间】:2016-08-25 05:27:26 【问题描述】:这是我遇到的一个非常有趣的问题:有一个有向树,其中每个节点的权重随时间变化,我必须找到从根到某个节点的距离。
问题陈述:
售票柜台前排起了长队。这是队列 考虑因素。 最多可以有 2 个传入队列在一个交汇点合并 从任何交汇点只能有一个传出队列 可以有多个交汇点和队列移动 单向 将只有一个最终售票柜台点,所有的 排队领队 有多个入口点供粉丝到达 计数器 我需要设计一个可以向粉丝推荐的系统 “最佳路径”及其到达柜台的“预期时间”从一个队列到达柜台的预期时间取决于该队列中的人数加上其他队列中的人数。
穿过售票柜台并收到票的时间为 1 时间单位 假设每个路口都有一名警察 谁的工作是打开路口的大门,把人从 in-queue(s) 到 out-queue 。如果有多个队列 路口,警察会从每个队列中一一送粉丝 或者例如,如果有 2 个队列,每个队列包含 3 个粉丝,则队列 1 的领头人将首先被发送,然后是队列 2 的领头人,然后是队列 1 的下一个人,依此类推。这是传入队列之间的替代选择。
Full Problem Statement
对于给定的输入
第一行包含交汇点的数量 第二行包含队列数 接下来的“e”行包含三个值:开始连接点、结束点 路口和此队列中的人数。 (这也是此队列中可以站立的最大人数。)计算一个人到达即将进入任何队列的售票柜台的最短时间。另外,输出在最坏情况下他应该在最短的时间内到达柜台的路径(在每个交汇点,警察开始从队列中选择人,而不是我们正在计算的人的最短时间)。
如何解决这类时变问题?
例如:
7
6
1 5 9
2 5 5
3 6 1
4 6 3
5 7 7
6 7 4
图表如下所示:
售票点:7
入口点:1、2、3、4
刚从入口进入队列所需的时间 第3点:队列中的1人(3,6)+队列中的2人(4,6)+ 4 队列中的人(6,7)+队列中的 7 人(5,7)+ 队列中的 1 人 queue(1,5) 会先于这个人。最佳时间 = 15
路径是 3 -> 6 -> 7
【问题讨论】:
点击链接要求我登录 HackerEarth。您能否提供一些证据证明这不是当前编程竞赛的一部分,人们无需在那里注册即可验证? (例如截图) @j_random_hacker 我有一个帐户,刚刚检查过。它说“挑战结束,这个问题已经转移到练习区。你可以在这里提交你的解决方案或去练习区。另外提交不会影响排行榜。” 只是想。有什么理由不能简单地获取最短路径吗? 感谢@mk。 有趣的是,我想相反是一个更好的组织。一个入口(排队)和几个出口(售票处)。 【参考方案1】:这个问题可以通过找到每个入口节点(叶子)到出口节点(根)的最短路径来解决。
在下面的实现中,我使用邻接矩阵来表示这种(有向)图,但您可以将其视为二叉树(因为问题为每个连接点定义了最多 2 个输入队列)。
伪代码
int shortestPath(root)
if (both childs exists)
return min(shortestPath(node->left),shortestPath(node->right))*2+1
if (left child exists)
return shortestPath(node->left)
if (right child exists)
return shortestPath(node->right)
return 0; //no childs
正常最短路径和这个问题的唯一区别是,每当我们有两个进入队列时,警察会轮流从每个队列中一个接一个地发送粉丝。这意味着为了通过该队列,将需要 双倍的时间 +1。 +1 是因为我们假设他从较长的队列路径开始。
C++ 代码
这是一个有效的 C++ 代码,它返回一对包含最佳时间及其路径的代码。如果有多个最优路径,它将只返回其中一个。
const pair<int,vector<int>>& min(const pair<int,vector<int>>& a, const pair<int,vector<int>>& b)
return (a.first < b.first) ? a : b;
pair<int,vector<int>> findShortestPath(vector<vector<int>>& graph, int v)
vector<pair<int,vector<int>>> childs;
for (int i=0; i<v; i++)
if (graph[i][v] != -1)
pair<int,vector<int>> path = findShortestPath(graph,i);
path.second.push_back(v+1);
childs.push_back(make_pair(path.first + graph[i][v], path.second));
if (childs.size() == 2)
pair<int,vector<int>> path = min(childs[0],childs[1]);
return make_pair(path.first*2+1, path.second);
if (childs.size() == 1)
return make_pair(childs[0].first,childs[0].second);
else
vector<int> start = v+1;
return make_pair(0,start);
此代码的时间复杂度是O(n^2)
,其中n
是顶点数。您还可以在O(n)
中使用邻接表表示(= 二叉树)来实现它。
为了完整起见,这里还有main
,用于根据给定的输入创建图表并打印最佳时间和路径。 See this live test of your example's input
int main()
int n, e;
cin >> n; //num of vertices
cin >> e; //num of queues
vector<vector<int>> graph;
//initialize graph matrix cells to -1
graph.resize(n);
for (int i=0;i<n;i++)
graph[i].resize(n);
for (int j=0;j<n;j++)
graph[i][j] = -1;
//add edges and their weights
for (int i=0;i<e;i++)
int s,d,val;
cin >> s >> d >> val;
graph[s-1][d-1] = val;
//run algorithm
pair<int,vector<int>> path = findShortestPath(graph, n-1);
//print results
cout << path.first << endl;
for (int i=0;i<path.second.size()-1;i++)
cout << path.second[i] << " -> ";
cout << path.second[path.second.size()-1] << endl;
return 0;
【讨论】:
看来你的解决方案是正确的。您能否解释一下为什么简单的最短路径算法可以正常工作的背后的要点,因为每个边缘的人都随着时间而变化。 例如,如果您站在一个进入某个节点x
的队列中,并且还有另一个队列进入该节点。你需要多长时间才能通过那个节点?由于我们在这些队列之间交替,它将花费您的队列长度的两倍。我如何确定另一个队列中有足够的人来轮换?因为从上一次递归调用中,我们选择站在最小路径上,这意味着另一条路径中必须有更多的人。当只有一个队列进入节点时,通过它的时间就是队列的长度。【参考方案2】:
这应该通过动态规划来解决。 设 P(j) 为人的位置,如果取最优扇形,则通过结点 j。例如,在您的情况下 P(6) = 4,因为到达 3 号交界点的人将是第 4 个通过交界点 6 的人(在 P27、P26 和 P28 之后)。 下面三个命题很明显,解决了问题。 如果“连接点” j 是风扇,则 P(j) = 1(基本情况) 如果“连接点” j 是与孩子 l 和 r 的适当连接点,并且在 l 和 j 之间的队列中有 x 人,在 r 和 j 之间的队列中有 y 人,我们有 P(j) = 2 * min( P(l) + x , P(r) + y) 如果有人第 n 次通过柜台,则需要 n-1 次才能到达那里。 您可以使用 DP 轻松获得时间,并通过一些簿记(如果在左侧或右侧达到最小值),您可以获得最佳风扇。
【讨论】:
以上是关于有向树的叶到根的最短距离的主要内容,如果未能解决你的问题,请参考以下文章
如果您有一个拓扑有序图,基于到根的最大距离的稳定排序会保留拓扑顺序吗?