根据每对的总加权重量,有效地计算来自两个加权集的对

时间:2012-10-12 18:27:18

标签: c++ algorithm language-agnostic

假设我有两组数据,每个条目都包含一个权重。每组按重量递增排序。我将列出几个示例集:

Set 0: {4, 8, 19, 25, 25, 26}    
Set 1: {1, 2, 3, 8, 20, 27}

我想找到这两组之间的所有可能的对,但我想按照从最小到最大的总重量的顺序找到这些对。所以4 + 1,4 + 2,4 + 3,4 + 1等你可以假设这些集合是c ++ std::multiset,但除此之外,我认为它在任何语言中都是相同的。

现在,我考虑过有2个迭代器,并按顺序从第一个与每个第二个配对开始迭代,但不会从最小的总和权重开始按顺序计算每个对,因为Set 0: 4 + Set 1: 8>在此示例中,Set 0: 8 + Set 1: 1。我总是可以将结果转储到一个为我做排序的容器中,但这似乎效率低下。我还有其他一些优化,这些优化也取决于能否做到这一点。有没有一种聪明的方法来做到这一点,最后没有额外的排序?

编辑:我需要获得所有可能的配对,因此它不像递增一个迭代器那样简单,也不是为了获得最小的总和。这将错过大多数(一半?)的对。虽然可能有某种迭代器堆栈......

2 个答案:

答案 0 :(得分:2)

#include <iostream>
#include <set>
#include <vector>
#include <limits>

std::multiset<int> set1 {4, 8, 19, 25, 25, 26};
std::multiset<int> set2 {1, 2, 3, 8, 20, 27};

int main()
{
  std::vector<std::pair<std::multiset<int>::const_iterator,
    std::multiset<int>::const_iterator>> setIterators;
  for (auto i = set1.begin(); i != set1.end(); ++i)
    setIterators.push_back(std::make_pair(i, set2.begin()));

  for (std::size_t count = set1.size() * set2.size(); count != 0; --count)
  {
    int minValue = std::numeric_limits<int>::max();
    auto minElement = setIterators.begin();
    for (auto i = setIterators.begin(); i != setIterators.end(); ++i)
    {
      if (i->second == set2.end())
        continue;
      int sum = *i->first + *i->second;
      if (sum < minValue)
      {
        minValue = sum;
        minElement = i;
      }
    }
    std::cout << *minElement->first << " + " << *minElement->second << " = " <<
      minValue << std::endl;
    ++minElement->second;
  }
  return 0;
}

输出:

$ g++ -std=c++11 main.cpp -o main && ./main
4 + 1 = 5
4 + 2 = 6
4 + 3 = 7
8 + 1 = 9
8 + 2 = 10
8 + 3 = 11
4 + 8 = 12
...
26 + 27 = 53

答案 1 :(得分:2)

让我们表示2个排序列表A(大小m)和B(大小n)。

算法:

  1. 计算所有i = 0到n - 1的A [0]和B [i]之和。您需要确定列表A中哪个元素包括 - 单向是附加索引(这里所有总和为0)。将所有对推入一个由总和排序的最小堆。
  2. 弹出堆,取出总和。使用附加的索引来计算列表A中下一个元素的总和。如果索引已经是m - 1,则无需执行任何操作;否则增加索引并将其推回堆中。
  3. 重复步骤2,直到堆为空(对于所有m * n值)。
  4. 步骤1将为O(n log n)。

    步骤2最多为O(log n),因为堆的大小可能会减少而且永远不会增加。重复步骤2 m * n次导致时间复杂度为O(mn log n)。

    总体复杂度为O(mn log n)。

    通过在上面的算法中使用较小的列表作为列表B,我们可以实现稍微好一点的时间复杂度(我们只需要管理一个小堆而不是一个大堆)。

    使用std::priority_queueStacker)的实现:

    #include <iostream>
    #include <set>
    #include <queue>
    #include <limits>
    
    std::multiset<int> set1 {4, 8, 19, 25, 25, 26};
    std::multiset<int> set2 {1, 2, 3, 8, 20, 27};
    
    struct Node
    {
      Node(std::multiset<int>::const_iterator set1Iterator, int set2Value) :
        set1Iterator(set1Iterator),
        set2Value(set2Value),
        sum(*set1Iterator + set2Value)
      {
      }
    
      bool operator < (const Node& other) const
      {
        return sum > other.sum; // Reverse order as std::priority_queue sorts for the greatest value
      }
    
      std::multiset<int>::const_iterator set1Iterator;
      int set2Value;
      int sum;
    };
    
    int main()
    {
      std::priority_queue<Node> heap;
      for (auto i = set2.begin(); i != set2.end(); ++i)
        heap.push(Node(set1.begin(), *i));
    
      while (!heap.empty())
      {
        Node min(heap.top());
        heap.pop();
    
        std::cout << *min.set1Iterator << " + " << min.set2Value << " = " <<
          min.sum << std::endl;
        if (++min.set1Iterator != set1.end())
        {
          min.sum = *min.set1Iterator + min.set2Value;
          heap.push(min);
        }
      }
      return 0;
    }