【问题标题】:Creating a Maximal Configuration with Dynamic Programming使用动态规划创建最大配置
【发布时间】:2015-02-13 00:08:22
【问题描述】:

我一直在努力解决我在下面提出的这个问题。我有一些我尝试过的想法。我首先选择了 N 个元组的所有组合并对其进行排序,但实现很丑陋,而且速度很慢。我认为有一种动态编程方法可以解决这个问题。我在如何创建配置时遇到了麻烦。在那之后,我想我知道如何解决这个问题了。

问题陈述:

给定高度 H (1

问题要求找到堆栈的最大安全值。安全值是在不超过任何较低元组强度的情况下可以添加的权重量。如果不可能,就打印出-1。

输入:

输入的第一行包含N和H。

接下来的 N 行输入,每行描述一个元组,给出它的高度, 重量和强度。都是正整数,最多 10 亿。

样本输入:

4 10

9 4 1

3 3 5

5 5 10

4 4 5

样本输出:

2

【问题讨论】:

  • 请更好地解释问题。 H 起什么作用?你说的堆栈是什么?您可以尝试为示例输入解释这些内容,并展示示例输出如何是正确答案。
  • 你是对的,有一个动态编程解决方案。它首先将元组从最强到最弱排序。现在,当您构建堆栈时,您将始终将当前元组放在堆栈顶部。您永远不需要保留另一个堆栈,该堆栈具有至少一样高且断点至少一样低的堆栈。
  • 每个元组只能使用一次对吗?
  • 元组的数量级是多少?
  • 当你说给定高度H时,H是元组列表的最大高度吗?

标签: c++ algorithm dynamic-programming adhoc


【解决方案1】:

好吧,既然没有其他人发布解决方案,我会尝试一下。

给定两个元组t1 = (h1, w1, s1)t2 = (h2, w2, s2),我们可以将它们组合为
[t1 over t2][t2 over t1]。在每种情况下,我们都可以将结果视为另一个元组;例如,

t3 = [t1 over t2] = (h1 + h2, w1 + w2, min(s1, s2 - w1))

(结果元组的强度不能高于组件元组的两个强度中的任何一个,并且底部元组的强度会通过其顶部元组的权重而降低;结果强度可以是负数)。

无论组合顺序如何,生成的元组的高度和重量都是相同的;但是,最终的强度可能会因顺序而异。我们对最大强度的元组感兴趣,所以我们取两个可能值中的最大值。鉴于上述情况,让我们将组合定义为

t1 + t2 = (h1 + h2, w1 + w2, max(min(s1, s2 - w1), min(s2, s1 - w2)))

生成的元组又可以与其他元组组合,依此类推。

我们需要从至少H 的所有结果元组中找出最大强度,因为问题中要求的最大安全值实际上是结果元组的强度。

因此,我们可以将起始最大强度设置为-1,开始组合元组,并且每当我们发现高度为H 或更高时,如果元组的强度更大,则更新当前最大强度。

规则 1: 结果元组的强度不能高于组件元组的两个强度中的任何一个,因此,在组成元组时,每当我们发现其中一个强度小于或等于当前最大值,我们可以丢弃它,因为它将成为组件的元组的强度不能高于当前最大值。

规则 1a: 我们甚至可以丢弃用于更新当前最大强度的元组,因为问题不要求我们提供元组本身,只要求最大值和那个元组在任何其他组合中都不会产生更好的最大值。

规则 2: 现在,让我们从上往下看。任何n = 2k元组的栈都可以看成是由两个元组组成的,每个元组由一个k元组的栈组成;对于n = 2k + 1,两个堆栈的大小分别为kk + 1

所以我们按顺序构建:

  1. 初始元组列表;
  2. 由两个堆栈产生的元组列表;
  3. 由三个堆栈产生的元组列表,元组由列表 [1] 中的一个元组和列表 [2] 中的一个元组组成(所有组合,不包括使用主元组两次的组合);李>
  4. 由四个堆栈产生的元组列表,其中元组由列表 [2] 中的两个元组组成(同样,所有组合,不包括使用主元组两次的组合);

等等,直到N。在构建每个列表时,我们根据上述规则 1 对其进行修剪。

规则 1b: 每当更新最大强度时,所有现有列表都应从强度小于或等于新最大值的元组中剪除。每当这些元组作为组成新元组的一部分遇到时,这可以立即完成,也可以延迟完成。

就算法的描述而言,我认为就是这样。

在实现方面,我会将实际元组存储为std::tuplestruct,但有一点不同:对于每个生成的元组,您需要存储它所构建的主元组列表;我会为此使用std::vector<std::size_t>(包含第一个列表中的主元组的索引),因为然后您可以使用std::find_first_of 排除使用主元组两次的组合,甚至更好,如果您保留列表已排序,std::set_intersection

对于每个级别的列表,std::vector 也是如此。

当然,实际的 C++ 代码是您的工作。


注意:这里描述的算法具有非常糟糕的最坏情况复杂性特征。此解决方案的最坏情况意味着:大N、大H、与H 相比较小的元组高度、与其优势相比较小的元组权重。在这种情况下,上述规则中描述的修剪直到很晚才会生效,直到发生这种情况,我们才会出现组合爆炸。

但是,对于我认为更“有趣”的情况,高度、重量和力量的分布更均匀(类似于给出的示例),我认为这个解决方案的表现相当不错,即使与经典的动态规划相比也是如此解决方案,在这种情况下,可能类似于整数背包解决方案,其中一个条件倒置(还没有真正考虑过)。

我可能会在以后有时间做一些实际测量,只是出于好奇。

【讨论】:

  • 你设法理解了这个问题,并把这样的心放在了答案上。这值得赞成......可惜我只有一个
猜你喜欢
  • 2012-11-12
  • 1970-01-01
  • 2010-12-16
  • 1970-01-01
  • 1970-01-01
  • 2018-01-21
  • 2016-07-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多