【发布时间】:2011-02-28 09:41:12
【问题描述】:
有两种数据类型:任务和动作。一个动作需要花费一定的时间才能完成,并且该动作由一组任务组成。一个任务有一组动作,我们的工作就是选择其中一个。所以:
class Task { Set<Action> choices; }
class Action { float time; Set<Task> dependencies; }
例如,主要任务可能是“买房子”。此任务的可能操作:“买房子”或“建房子”。 “建造房屋”动作需要 10 小时,并具有“获取砖块”(可能需要 6 小时)和“获取水泥”(需要 9 小时)等依赖项。
总时间是需要执行的所有动作的总和(在本例中为 10+6+9 小时)。我们希望选择总时间最短的动作。
请注意,依赖项可以是菱形的。例如,“获取砖块”可能需要“获取汽车”(运输砖块),而“获取水泥”也需要汽车。即使你做了“拿砖”和“拿水泥”,你也只需要计算得到一辆车所需的时间一次。
还要注意,依赖关系可以是循环的。例如“钱”->“工作”->“汽车”->“钱”。这对我们来说没问题,我们只需选择所有“Money”、“Job”和“Car”。总时间就是这三件事的时间之和。
数学描述:
让actions 成为选定的操作。
valid(task) = ∃action ∈ task.choices. (action ∈ actions ∧ ∀tasks ∈ action.dependencies. valid(task))
time = sum {action.time | action ∈ actions}
minimize time subject to valid(primaryTask)
我对最佳解决方案感兴趣,但也对近似解决方案感兴趣。也许某种动态编程可以提供帮助?如果问题是树结构的,那么动态规划可以在多项式时间内给出最优解,但菱形结构似乎使问题变得更加困难。如果你有一个算法,但如果有循环它就不起作用,请发布它!我可能仍然可以从中学到很多东西。
方框代表任务,圆圈代表动作(执行动作的时间在圆圈中)。如果该任务是该操作的依赖项,则该操作具有该任务的行。这里再次用图片来描述问题:如果选择了一个矩形(=task),那么必须选择里面的一个圆圈(=actions)。如果选择了一个圆,则必须选择所有个相连的矩形。目标是最小化所选圆圈中的数字总和。
在这种情况下,最佳解决方案是在顶部任务中选择时间为 2 的动作,在底部任务中选择时间为 1 的动作。总时间为 2+1+1=4。在这种情况下,有 2 个最优解。第二种解决方案是在顶部任务中选择时间为 3 的动作,在右下任务中选择时间为 1 的动作。总时间又是 3+1=4。如果我们在顶部任务中选择时间为 3 的动作,我们不必执行左下任务,因为时间为 3 的动作和左下任务之间没有界限。
对于糟糕的绘图,我深表歉意;)还有两个示例(每个示例的最佳解决方案已用蓝色表示,主要任务已用灰色表示):
【问题讨论】:
-
一个Action如何花费固定的时间(Action.time)?一个 Action 的时间不应该取决于它在 Action.dependencies 中完成每个 Tasks 所需的时间吗?
-
是的,你是对的,术语很不幸。一个动作所花费的总时间确实是 action.time + 所有依赖项的总时间。 action.time 只是那个动作的时间,不包括依赖的时间。
-
在问题中,您提到了一些任务只需要执行一次(“获取汽车”)的要求,而我假设每次需要时都必须执行一些任务(“拿砖”?)。 Task 的这个属性在你的数据结构中是如何表示的?
-
每个任务最多执行一次。如果需要多次执行,则数据结构中有多个副本(例如“为房子 1 获取砖块”和“为房子 2 获取砖块”)。这种情况几乎从来没有发生过。
-
您如何建模“为房子 1 拿砖”与“为房子 2 拿砖”?
标签: algorithm optimization math computer-science