【问题标题】:Best way for three people to visit some graph nodes in a given order三个人按给定顺序访问某些图节点的最佳方式
【发布时间】:2018-03-27 08:36:51
【问题描述】:

UPD。我解决了这个问题。

DP[i][vertex_a][vertex_b] 是访问过i 城市的状态,两个玩家站在顶点vertex_a, vertex_b (保证其中一个站在list[i])。 WLOG 假设vertex_a ≤ vertex_b 因为这个DP 表不包含有关球员位置的信息。从DP[i][vertex_a][vertex_b]只能到达三种状态,即DP[i + 1][vertex_a][vertex_b]DP[i + 1][list[i]][vertex_b]DP[i + 1][vertex_a][list[i]]。我们也只需要存储两层DP,因此只需要sizeof(int) * 2 * 200 * 200字节来计算最优路径成本。要获得路径,last_move_id[i][vertex_a][vertex_b] 将携带有关在状态DP[i][vertex_a][vertex_b] 移动的玩家的信息,last_move_positions[i][vertex_a][vertex_b] 存储玩家到达list[i] 的顶点数。由于顶点数不超过 200,因此将它们保存为 byte,因此每个数组的 sizeof(byte) * 1000 * 200 * 200 字节。为了维护这些数组,必须有另一个数组positions[i][vertex_a][vertex_b][3] 携带有关每个玩家位置的信息,只需要最后两层,因此sizeof(byte) * 2 * 200 * 200 * 3 字节为这一层。时间复杂度O(N * L * L)

我的C++ 实现使用76Mb320 ms

我正在努力解决来自俄罗斯在线评委http://informatics.mccme.ru/moodle/mod/statements/view.php?chapterid=3379 的以下竞争性编程问题。按照规定,我记得一定要提供问题的根源

很遗憾,没有英文版的网站,所以我将尝试描述问题。

输入由完整的有向图GL 顶点和一些顶点列表(长度最多N)组成。三个人分别从顶点1, 2, 3 开始。他们必须访问输入列表中的每个顶点,顺序很重要,顶点i + 1 必须在i 之后访问。在某个时间点,只有一个人可以移动(如果一个人从某个先前的顶点移动到顶点i 其他人静止不动,他们不能平行移动)。如果人/玩家站在顶点 i 并且必须移动到顶点 j 他必须采取边缘 (i, j) 而不是到顶点 j 的最短路径(Floyd-Warshall 算法不能用来加速这里的计算) .一个人可以访问一个顶点就足够了,这意味着人 1 可以访问所有顶点,而其他人则静止不动。边(i, i) 的成本始终为0,没有多重边,所有边的权重都是非负的,G 表示为L x L 邻接矩阵。从列表中输出这三个人访问顶点的最短路径成本,并输出访问每个顶点的人。输入要访问的顶点列表是一个多重集(N 可能大于L

我发现这个问题有点类似于 城市序列的两人遍历 问题,除了这是一个 三人 版本并且它们以不同的开头位置和一个主要区别是人们必须访问允许重复的特定顶点序列。我已经研究过这个问题的解决方案,时间复杂度是O(L^3) 对于城市序列的两人遍历,我的问题是O(N^4),这太慢了,因为即使O(N^3)算法也不会满足时间限制,我想像@ 987654365@ 可以工作。

约束:

3≤L≤200,1≤N≤1000

0≤边权≤2000

时间限制:1s,内存限制为256 Mb

我也确信这个问题可以使用 64 Mb 来解决。

这个问题被标记为2D dynamic programming

我无法真正想出这个确切的2D 动态。我想到了一个非常简单的方法来解决这个问题:

三个人的初始状态是(1, 2, 3)。在处理我们计算的第一个顶点时:

1:(list[1], 2, 3) = (1, 2, 3) + weight(1, list[1])

1:(1, list[1], 3) = (1, 2, 3) + weight(2, list[1])

1:(1, 2, list[1]) = (1, 2, 3) + weight(3, list[1]).

正如人们所看到的,这是一个4D 动态表,但我认为保持当前迭代的数量是不必要的,使其成为3D 之一。此外,人们可以注意到,对于计算 (i+1)-th 层,第一层只需要关于 i-th 层的信息,这使得它成为一个很好的内存优化。尽管如此,如果我们忘记图中最多只有 200 个顶点,并将状态视为元组(i, j, k),其中 i,j,k 是最后阶段玩家 1、2 的数量, 3 移动 at,这意味着在 m-th 阶段 i, j, k 之一等于 m。按照这个逻辑并考虑所有可能的重复,在 m-th 阶段的不同元组的数量是:

Number_at_stage(m) = Number_at_stage(m - 1) + 6 * (m - 1),Number_at_stage(1) = 3, Number_at_stage(2) = 9, Number_at_stage(1000) = 2991009.

Number_at_stage(1)我从以下想法得到:

(0, 0, 0) -> (1, 0, 0), (0, 1, 0), (0, 0, 1)

我已经将阶段1..1000 的不同元组的数量相加,得到了一个可怕的997004997 数量,几乎是十亿。这意味着代表此类移动的不同元组的数量是渐近三次的(并不奇怪,但很明显)。我不明白如何改进这个想法。以这种方式思考,我不知道如何处理诸如 (i, j, k) 和 (k, j, i) 之类的状态,因为它们实际上是等价的,因为可以基于相同的步骤集在这些上。我只是不知道如何处理这些状态并保存信息什么人访问了哪个城市(简单的多维数组?)。

我的下一个想法是有一个二维 DP(i, j) 存储从 i 到 j 元素的子列表的最佳距离总和。如果索引从 1 开始,答案将存储在 DP(1, N) 中。我可以计算长度为 1、2、... N 的所有子集。这个想法有一个主要问题,我不知道如何在不知道玩家可以站在的所有潜在位置的情况下处理 DP(i, j)(列表中的所有元素都在 i 之前以及初始位置 1、2、3)。我也不知道如何确定是哪个玩家用这种方法做出了动作。

您能帮我找到 2D 动态吗?

【问题讨论】:

    标签: algorithm dynamic-programming


    【解决方案1】:

    给定访问list[i] 时三个玩家的可能状态,以及迄今为止与每个此类状态相关的成本,计算list[i+1] 的可能状态和成本。当您到达list[N-1] 时,选择最低费用。保留前置链接,以便您可以遍历整个序列回到开头并输出它。

    我很确定你已经得到了这么多...这是你错过的部分:

    访问list[i] 时有多少个可区分的状态?嗯,它小于 L^3,因为哪个玩家在哪个顶点上并不重要。它也小于L^3/3!,因为至少有一个玩家显然在list[i]

    因此,一名玩家在list[i] 上,而其他玩家只有 L(L+1)/2 个可区分的位置。这意味着每个list[i] 最多有 20100 个可能的状态,并且在每个列表索引的整个可能性数组中大约有 20M 个可能的状态。如果您对存储状态和链接的方式稍加注意,则可以满足 256MB 的内存限制。

    【讨论】:

    • 谢谢,这些提示很有用。虽然评论很有帮助,但您误解了一件事(可能是因为我的表述不佳),如果人/玩家站在顶点 i 并且必须移动到顶点 j 他必须取一条边 (i, j) 而不是到顶点 j 的最短路径。但这绝不会损害您的想法
    • 我为此调整了文本,我认为您可能允许多个玩家在同一个顶点上,所以我也进行了调整。
    猜你喜欢
    • 1970-01-01
    • 2017-03-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多