我正在寻找一种算法来解决这个问题。我必须实现它(所以我需要一个非np解决方案XD)
我有一个完整图表,每个拱门都有成本,每个顶点都有奖励。我只有一个起点,但它并不重要,因为问题是要找到一条路径来尽可能多地查看顶点,以便尽可能获得最大的奖励,但需要付出最大的代价限制。 (因此,它与最终位置无关)。
我认为找到最佳解决方案是一个难以解决的问题,但也是一个近似的解决方案:D
由于
我正在尝试学习如何用branch& amp;绑定...
更新:完整问题dscription
我有一个区域,其中有几个区域由其id和x,y,z位置标识。每个顶点标识这些区域中的一个。最大数量为200。 从起点 S ,我知道在秒中指定并插入拱(因此只是整数值)的成本,以从每个其他顶点到达每个顶点(a完整图)。 当我访问一个顶点时,我得到了一个奖励(浮动的valiues)。
我的目标是在图表中找到最大化奖励的路径,但我在路径上受到成本约束的约束。事实上,我只有有限的时间来完成路径(例如600秒。)
图表是作为成本和奖励的矩阵邻接矩阵(但如果有用,我可以更改表示)。
我可以访问顶点更多时间,但只有一个奖励!
答案 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).
遗憾的是,这些限制相当复杂。我们首先将x
和y
变量关联起来。
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)
这是一种解决问题的递归方法。
让我们从一些定义开始:
以下是将输出确切请求的解决方案的递归过程:(伪代码)
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>
Target <- heap.pop()
。注意:哈希表最适合跟踪收集的节点。这样,我们可以检查在O(1)中使用Dijkstra计算的路径中的节点。
同样,在沿路径收集节点时,维护导致堆中每个节点位置的哈希表可能对优化堆的“修剪”很有用。
这种方法在复杂性方面略优于第一种方法,但可能无法获得最佳结果。实际上,它甚至可以在某些图形配置上表现很差。例如,如果所有节点都有一个奖励r,除了一个节点T对于每个节点N有r + 1和W(N↔T)= C,但是其他边缘都可以到达,那么这只会让你收集T并错过其他所有节点。在这种特殊情况下,最好的解决方案是忽略T并收集其他人,从而获得(N-1).r的奖励,而不是仅仅r + 1.