从一组{{..},{..},{..}}中选择一组数字{1,2,3},以便所有元素都是唯一的

时间:2016-09-30 10:27:28

标签: c++ algorithm

我对一个有效的算法感兴趣,可以完成以下工作:

  • 我有一组由三个数字组成的集合。
  • 在这样的子集中,数字是唯一的。
  • 但是它们可以在其他子集中多次出现。
  • 目标是选择例如3个子集,这样只有元素 发生一次。

例如:{{1,2,3},{3,4,5},{6,7,8},{5,7,9}}

  • 第二个子集与第一个子集共有3个。
  • 第4个子集具有与第2和第3子集相同的元素。
  • 现在选择了多个子集,但不可以。每个元素 只能发生一次。

我实施了一项能够完成工作的东西,但我想这可以更有效率地完成。怎么样?甚至可能有一个map左右的恒定时间算法?

  • 我使用list来实现集合,因为我删除了pick或 排除子集(快速删除)。
  • 子集使用vector s。
  • 实现
  • 要跟踪已选择的号码,我使用set

Watch it here.

#include <iostream>
#include <vector>
#include <list>
#include <set>
#include <iterator>
#include <algorithm>
#include <random>

using namespace std;

random_device rd;
mt19937 rng(rd());

// pickes a random entry from a vector and returns an iterator to that element.
list<vector<int>>::iterator randomEntry(list< vector<int> >& v)
{
  uniform_int_distribution<int> dis(0, v.size()-1); // guaranteed unbiased
  auto it{ begin(v) };
  advance(it, dis(rng));
  return it;
}

int main()
{
  // a collection of possible sets
  list< vector<int> > collection;
  collection.emplace_back( vector<int>{51,9,22} );
  collection.emplace_back( vector<int>{11,5,74} );
  collection.emplace_back( vector<int>{61,9,35} ); // 2nd element in common with 1st entry
  collection.emplace_back( vector<int>{19,54,66} );
  collection.emplace_back( vector<int>{53,86,35} ); // 3rd element in common with 3rd entry
  collection.emplace_back( vector<int>{11,3,55} ); // 1st element in common with 2nd entry

  // pick three -independent- sets form the collection
  vector< vector<int> > picked;
  set<int> elements;

  while(picked.size()<3)
  {
    auto entry{ randomEntry(collection) }; // iterator to a randomly choosen entry

    bool unused{ true };
    for(const auto i : *entry)
    {
      if( elements.find(i) != cend(elements)) // if it already exists in elements, its already used.
        unused= false;
    }
    if(unused)
      picked.emplace_back( *entry );
    collection.erase(entry); // in any chase, remove == don't pick it again.
  }

  // all the elements printed should only appear once.
  for(const auto& i : collection)
  {
    for(const auto j : i)
      cout<<j<<" ";
    cout<<endl;
  }

  return 0;
}

2 个答案:

答案 0 :(得分:1)

  

我有一组由三个数字组成的集合。

     

......目标是选择,例如3个子集,这样元素只发生一次。

     

......但我想这可以更有效率地完成。怎么样?

由于您的目标是选择不相交的子集的任意数 p (以3为例),这正是Set Packing problemKarp's 21 NP-complete problems之一}。

在这个问题中,你提到每个集合都有3个元素(这次不是一个例子)。不幸的是,这个版本仍然是NPC。但幸运的是,这意味着there is an approximation algorithm with with a factor of ~50%

因此,您不太可能找到一个多项式时间算法来解决一般 p 。看看你的代码,我怀疑它是否正确。它是一种(随机)贪婪算法,似乎有可能构建场景(输入+随机选择),悲观地认为没有解决方案。

答案 1 :(得分:0)

创建一个地图(用c ++编写哈希)

迭代集合。

对于每个集合,检查其中一个号码是否在地图中。  如果没有将数字添加到地图并将此集添加到唯一集 如果它在地图中,继续下一组。