在列表中对数字进行分组

时间:2017-09-17 07:45:25

标签: algorithm

我遇到了以下问题,

您将获得n个元素的数组A.现在,这些元素被添加到新列表L中,该列表最初为空,基于给定的q查询按特定顺序。

  • 在每个查询中,您将获得一个整数i,它对应于数组A中的A [i]。这意味着您必须将元素A [i]添加到列表L中。
  • 将每个元素添加到列表L后,在列表L中的元素之间创建组。如果数组A中的索引是连续的,则两个元素将在同一组中。
  • 对于每个组,我们将组的值定义为 axb ,其中a是该组中的最大值,b是该组的大小。

在将每个元素添加到列表L后,在所有组中打印最大组值。

我的方法是使用>[1]: sel.xpath("//div[@id='branchprofile']/script/text()")[0].extract() <[1]: '\n(function(k,v){RMVH.ANALYTICS.DataLayer.pushKV(k,v);}(\'branch\',{"branchId":5112345,"companyName":"KLM","brandName":"London KLM",,"pageType":"Standard"})); ' ,其中key是组号,value是包含组大小max的向量。小组我也有一个数组g和g [i]表示a [i]的组号,如果它不在任何组中,则为-1。下面的代码是我实现的一部分,但我确信有更好的方法可以解决这个问题,因为我的解决方案在某些情况下给了TLE和WA,我似乎无法弄清楚正确的方法。请提出解决此问题的最佳方法。

map<int,vector<int>>

2 个答案:

答案 0 :(得分:0)

我实际上不会构建列表L.在找到新值时可能会花费太多代价:它本身是一个新组,它是否扩展了现有组,是否需要合并为两个组一?如果第一个值相距很远,那么您将拥有多个组,并且需要使用每个新的传入值进行迭代:这样效率不高。

我会先收集所有值,然后再看看它们是如何组合的。

有两种收集价值的方法:

  1. 将它们存储在列表中,并在收集完所有值后,按升序对列表进行排序

  2. 在大小为 n 的布尔数组中标记该条目。这样您就不必对它进行排序,但之后您需要迭代整个数组以按升序查找值。

  3. q 远小于 n 时,方法1将是最佳的。方法2对于更大的 q 更好。

    使用这两种方法,您将能够按升序迭代找到的值,同时这样做可以识别组,它们的值,并跟踪最大的组值。只需要一次扫描即可找到答案。

答案 1 :(得分:0)

让我们从两个简化的假设开始:

  • 没有重复。一旦给定索引i被“查询”,它将永远不会再被查询。
  • 没有负数。所有元素都是正数或零,因此组中的最大值始终为正或零,因此扩展组(或合并两个组)将永远不会导致整体“最大组值”降低。

(下面我将展示如何需要这些假设,但现在这将简化图片。)

因此,每当我们“查询”索引i时,就有四种情况:

  • i-1目前是一个组的右端点(我的意思是它的最大索引),而i+1当前是另一个组的左端点。
    • 在这种情况下,我们需要将两个组合并为一个组,i弥合它们之间的差距。
  • i-1目前是群组的右端点,但i+1目前不在任何群组中。
    • 在这种情况下,我们需要将小组扩展为i
  • i-1目前不在任何群组中,但i+1目前是群组的左端点。
    • 在这种情况下,与前一种情况一样,我们需要将该群组扩展为i
  • i-1i+1都不属于某个群组。
    • 在这种情况下,我们有一个只有一个元素的新组。

在所有情况下,需要注意的关键是我们只对组的端点感兴趣。因此,我们不需要从索引到其组的一般映射。 。 。这很好,因为当我们合并两个组时,那么从一个组更新每个索引以指向另一个组将是昂贵的。

所以我们只需要三个映射:

std::unordered_map<int, int> map_from_left_endpoint_to_right_endpoint;
std::unordered_map<int, int> map_from_right_endpoint_to_left_endpoint;
std::unordered_map<int, int> map_from_left_endpoint_to_largest_value;

为区分这四种情况,我们使用例如map_from_right_endpoint_to_left_endpoint.find(i - 1)(返回指向i-1是右端点的组的左端点的迭代器(如果适用);否则返回map_from_right_endpoint_to_left_endpoint.end())。然后,我们删除条目,因为它们变得不再适用(由于组在给定方向上被扩展或合并),此外(显然)插入新条目,并更新现有条目的值。

除了这些值,我们还需要一个

int maximum_group_value = 0;

每当我们扩展一个组或合并两个组时,我们检查结果组的值(意味着它的largest_value * (right_endpoint - left_endpoint + 1)是否大于maximum_group_value。如果是,我们更新maximum_group_value并将其返回;如果没有,我们按原样返回maximum_group_value

现在,如果允许重复 ,那么在给定索引i已经属于某个组之后可能会被“查询”该怎么办?

最简单的方法是简单地跟踪已查询的i - s;但如果需要,更优雅的方法可能是将map_from_left_endpoint_to_right_endpointstd::unordered_map更改为std::map,然后使用以下内容:

bool is_already_in_a_group(
    std::map<int, int> const & map_from_left_endpoint_to_right_endpoint,
    int const i) {
  // get iterator to first element *after* index (or to 'end()' if no such):
  auto iter = map_from_left_endpoint_to_right_endpoint.upper_bound(index);
  // if that pointer points to 'begin()', then there are no elements
  // at or before index:
  if (iter == map_from_left_endpoint_to_right_endpoint.begin()) {
    return false;
  }
  // otherwise, move iterator to point to the last element whose key is
  // less than or equal to index:
  --iter;
  // . . . and check whether the value of that element is greater than
  // or equal to index (meaning that [key, value] spans index):
  return iter->second >= index;
}

检查map_from_left_endpoint_to_right_endpoint中小于或等于i的最大键是否映射到大于或等于i的值。

这为我们上面的案例分析添加了第五个案例 - “如果i已经在一个组中,则不做任何事情并返回maximum_group_value” - 但除此之外,没有任何效果。

请注意,如果我们愿意,同样的方法也可以让我们删除map_from_right_endpoint_to_left_endpoint:通过将int get_left_endpoint_for_right_endpoint语句更改为return,可以轻松地将上述函数调整为return iter->second == index ? iter->first : -1;

此时,定义一个包含三个字段Groupleft_endpointright_endpoint)的largest_value类变得明智,只需保留一个{{1} }}

最后 - 如果允许负值,那么“最大组值”实际上可以减少作为查询的结果怎么办? (例如,如果数组元素为map_from_left_endpoint_to_group且查询为[-1, -10]i=0,则结果为i=1maximum_group_value=-1。)在此类中例如,我们需要跟踪所有当前组的值,因为它们中的任何一个都可能突然变为最大值。

为此,我们可以维护一堆按值排序的组,而不是存储单个maximum_group_value=-2,我们在每次创建/扩展/合并组时都会进入这些组。 (我们可以使用int maximum_group_value加上std::vector<Group>加上适当的比较器,或者使用适当的std::push_heap定义。)在每次查询之后,我们检查顶部组是否在heap(向量中的第一个元素)仍然是一个实际存在的组;如果是这样,我们返回它的值,否则我们弹出它(使用operator<(Group const &, Group const &))并重复。

作为优化,我们存储std::pop_heap,并在遇到非负数组元素时消除堆(因为只要给定的组包含非负数组-element,它的值永远不会再次减少,显然最大组值将是其中一个组的值。)