叶子到有向树的根的最短距离

时间:2016-08-25 05:27:26

标签: algorithm graph tree shortest-path

这是我遇到的一个非常有趣的问题:有一个有向树,每个节点的权重随时间变化,我必须找到从根到某个节点的距离。

问题陈述:

  • 售票柜台前排起了很长的队列。这是队列 考虑。
  • 在联结点合并最多2个传入队列
  • 任何联结点只能有一个传出队列
  • 可以有多个交接点&队列移动 单向
  • 只有一个最终的票务柜台点所有的 排队领先
  • 粉丝有多个入口点可以到达 计数器
  • 我需要设计一个可以向粉丝们建议的系统 到达柜台的“最佳路径”及其“预期时间”

从队列到达计数器的预计时间取决于该队列中的人数加上其他队列中的人数。

  • 越过售票柜台并收到机票的时间是1 时间单位
  • 假设有一名警察站在每个交叉点 他的工作是打开交界门,让人们从 队列中的队列。如果a有多个队列 交界处,警察将逐一向每个队列发送粉丝 可替代地

例如,如果有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

图表看起来像这样: enter image description here

售票点:7

入口点:1,2,3,4

  • 刚进入队列的人所需的时间 第3点:队列中的1人(3,6)+队列中的4人(4,6)+ 4 队列中的人(6,7)+队列中的7人(5,7)+ 1人 队列(1,5)将在此人之前。

最佳时间= 15

路径是3 - > 6 - > 7

2 个答案:

答案 0 :(得分:5)

通过从每个入口节点(离开)到出口节点(root)找到最短路径,可以解决此问题。

在我下面的实现中,我使用了一个邻接矩阵来表示那种(有向)图,但是你可以把它想象成一个二叉树(因为这个问题为每个连接定义了最多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是因为我们假设他从较长的队列路径开始。

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;
}

答案 1 :(得分:-2)

这应该通过动态编程来解决 设P(j)是人的位置,如果需要最佳风扇,则通过连接点j。例如,在你的情况下P(6)= 4,因为到达交叉点3的人将是第4个通过交叉点6(在P27,P26和P28之后)。
以下三个命题是显而易见的,并解决了这个问题 如果是一个&#34;交界点&#34; j是扇形,P(j)= 1(基本情况)
如果是一个&#34;交界点&#34; j是与子l和r的正确连接点,并且在r和j之间的队列中l和j之间的队列中有x个人,我们有P(j)= 2 * min(P(l) + x,P(r)+ y)
如果有人要通过柜台,那么到达那里需要n-1次 您可以轻松地使用DP和一些簿记(如果在左侧或右侧达到分钟),您可以获得最佳风扇。