在包含成本限制和最大奖励的完整图表中查找路径

时间:2014-12-19 10:38:52

标签: algorithm graph

我正在寻找一种算法来解决这个问题。我必须实现它(所以我需要一个非np解决方案XD)

我有一个完整图表,每个拱门都有成本,每个顶点都有奖励。我只有一个起点,但它并不重要,因为问题是要找到一条路径来尽可能多地查看顶点,以便尽可能获得最大的奖励,但需要付出最大的代价限制。 (因此,它与最终位置无关)。

我认为找到最佳解决方案是一个难以解决的问题,但也是一个近似的解决方案:D

由于

我正在尝试学习如何用branch& amp;绑定...

更新:完整问题dscription

我有一个区域,其中有几个区域由其id和x,y,z位置标识。每个顶点标识这些区域中的一个。最大数量为200。 从起点 S ,我知道在中指定并插入拱(因此只是整数值)的成本,以从每个其他顶点到达每个顶点(a完整图)。 当我访问一个顶点时,我得到了一个奖励(浮动的valiues)。

我的目标是在图表中找到最大化奖励的路径,但我在路径上受到成本约束的约束。事实上,我只有有限的时间来完成路径(例如600秒。)

图表是作为成本和奖励的矩阵邻接矩阵(但如果有用,我可以更改表示)。

我可以访问顶点更多时间,但只有一个奖励!

2 个答案:

答案 0 :(得分:2)

由于你对分支和绑定感兴趣,让我们制定一个线性程序。使用Floyd-Warshall最小化向下调整成本,以便cost(uw) ≤ cost(uv) + cost(vw)为所有顶点u, v, w

s成为起始顶点。我们有0-1变量x(v)表示顶点v是否是路径的一部分,0-1变量y(uv)表示弧uv是否是路径的一部分。我们寻求最大化

sum over all vertices v of reward(v) x(v).

遗憾的是,这些限制相当复杂。我们首先将xy变量关联起来。

for all vertices v ≠ s,  x(v) - sum over all vertices u of y(uv) = 0

然后我们限制了成本。

sum over all arcs uv of cost(uv) y(uv) ≤ budget

我们有(预)流限制,以确保选择的弧看起来像一个可能伴随循环的路径(我们将很快处理循环)。

for all vertices v,  sum over all vertices u of y(uv)
                       - sum over all vertices w of y(vw)
                         ≥ -1 if v = s
                            0 if v ≠ s

为了处理周期,我们添加了切割覆盖约束。

for all subsets of vertices T such that s is not in T,
  for all vertices t in T,
    x(t) - sum over all vertices u not in T and v in T of y(uv) ≥ 0

由于预流限制,循环必然与路径结构断开连接。

存在指数级的削减覆盖约束,因此在解决LP时,我们必须按需生成它们。这意味着找到s和彼此顶点t之间的最小切割,然后验证切割的容量不大于x(t)。如果我们发现违规,那么我们添加约束并使用双重单纯形法找到新的最优值(必要时重复)。

我将继续描述分支机制 - 无论如何,这应由你的LP求解器来处理。

答案 1 :(得分:0)

寻找最佳解决方案

这是一种解决问题的递归方法。

让我们从一些定义开始:

  • 设A =(A i 1≤i≤N为区域。
  • 设w i,j = w j,i 从A i 到A j旅行的时间成本,反之亦然。
  • 让r i 参观区域的奖励A i

以下是将输出确切请求的解决方案的递归过程:(伪代码)

List<Area> GetBestPath(int time_limit, Area S, int *rwd) {
    int best_reward(0), possible_reward(0), best_fit(0);
    List<Area> possible_path[N] = {[]};
    if (time_limit < 0) {
        return [];
    }
    if (!S.visited) {
        *rwd += S.reward;
        S.visit();
    }
    for (int i = 0; i < N; ++i) {
        if (S.index != i) {
            possible_path[i] = GetBestPath(time_limit - W[S.index][i], A[i], &possible_reward);
            if (possible_reward > best_reward) {
                best_reward = possible_reward;
                best_fit = i;
            }
        }
    }
    *rwd+= best_reward;
    possible_path[best_fit].push_front(S);
    return possible_path[best_fit];
}

出于明显的清晰度原因,我认为A i 是全局可达的,以及w i,j

说明

您从 S 开始。你做的第一件事?收集奖励并将节点标记为已访问。然后你必须检查 S 的N-1个邻居之间的最佳路径(让我们称之为N S,i 为1≤i≤N-1 )。

这与解决N S,i 的问题完全相同,时间限制为:

  

time_limit - W(S↔N S,i

由于您标记了访问过的节点,因此在到达某个区域时,首先要检查它是否已标记。如果是这样,你没有奖励......否则你收集并标记为访问...

等等!

结束条件是time_limit(C)变为负数。这告诉我们达到了极限并且无法进一步移动:递归结束。如果在达到时间限制C之前已经收集了所有奖励,则最终路径可能包含无用的旅程。你必须“修剪”输出列表。

复杂性?

哦,这个解决方案在复杂性方面太糟糕了! 每次通话都会导致N-1次通话......直到达到时限。通过每次在最短边缘来回来回产生最长的呼叫序列。设w min 为该边的权重。

然后显然,整体复杂性受N C / w min .C / w min 的限制。
这是huuuuuge。


另一种方法

维护所有访问节点的哈希表。 另一方面,维护尚未收集的节点的最大优先级队列(例如,使用 MaxHeap )。 (堆的顶部是奖励最高的节点)。队列中每个节点A i 的优先级值设置为一对(r i ,E [w i,j] )< / p>

  1. 弹出堆:Target <- heap.pop()
  2. 使用Dijkstra算法计算到此节点的最短路径。
  3. 检查路径:如果路径的开销太高,则无法访问该节点,请将其添加到无法访问的节点列表中。
    1. 否则收集您在其中找到的所有未收集的节点......
    2. 从堆中删除每个收集的节点。
    3. 将目标设定为新的起点。
  4. 在任何一种情况下,请继续执行步骤1.直到堆为空。
  5. 注意:哈希表最适合跟踪收集的节点。这样,我们可以检查在O(1)中使用Dijkstra计算的路径中的节点。

    同样,在沿路径收集节点时,维护导致堆中每个节点位置的哈希表可能对优化堆的“修剪”很有用。

    一点分析

    这种方法在复杂性方面略优于第一种方法,但可能无法获得最佳结果。实际上,它甚至可以在某些图形配置上表现很差。例如,如果所有节点都有一个奖励r,除了一个节点T对于​​每个节点N有r + 1和W(N↔T)= C,但是其他边缘都可以到达,那么这只会让你收集T并错过其他所有节点。在这种特殊情况下,最好的解决方案是忽略T并收集其他人,从而获得(N-1).r的奖励,而不是仅仅r + 1.