Listing specific subsets using STL

2019-09-14 05:23发布

问题:

Say I have a range of number, say {2,3,4,5}, stored in this order in a std::vector v, and that I want to list all possibles subsets which end with 5 using STL... that is :

2 3 4 5
2 3 5
2 4 5
3 4 5
2 5
3 5
4 5
5

( I hope i don't forget any:) )

I tried using while(next_permutation(v.begin(),v.end())) but didn't come up with the wanted result :)

Does anyone have an idea?

PS : those who have done the archives of google code jam 2010 may recognize this :)

回答1:

tomasz describes a solution which will be workable as long as n<=32 although it will be take a very long time to print 2^32 different subsets. Since the bounds for the large dataset are 2 <= n <= 500 generating all the subsets is definitely not the way to go. You need to come up with some clever way to avoid having to generate them. In fact, this is the whole point of the problem.

You can probably find solutions by googling the problem if you want. My hint is that you need to look at the structure of the sets and avoid generating them at all. You should only calculate how many there are.



回答2:

Let's focus on the problem of printing all subsets. As you know, if you have vector of n elements, you'll have 2^n possible subsets. It's not coincidence, that if you have n-bit integer, the maximal stored value is 2^n. If you consider each integer as a vector of bits, then iterating over all possible values will give all possible subsets of bits. Well, we have subsets for free by iterating integer!

Assuming vector has not more than 32 elements (over 4 billion possible subsets!), this piece of code will print all subset of vector v (excluding empty one):

for (uint32_t mask =1; mask < (1<<v.size()); ++mask)
{
  std::vector<int>::const_iterator it = v.begin();
  for (uint32_t m =mask; m; (m>>=1), ++it)
  {      
    if (m&1) std::cout << *it << " ";
  }
  std::cout << std::endl;
}

I just create all possible bit masks for size of vector, and iterate through every bit; if it's set, I print appropriate element.

Now applying the rule of ending with some specific number is piece of cake (by checking additional condition while looping through masks). Preferably, if there is only one 5 in your vector, you could swap it to the end and print all subsets of vector without last element.

I'm effectively using std::vector, const_iterator and std::cout, so you might think about it as being solved using STL. If I come up with something more STLish, I'll let you know (well, but how, it's just iterating). You can use this function as a benchmark for your STL solutions though ;-)

EDIT: As pointed out by Jørgen Fogh, it doesn't solve your subset blues if you want to operate on large vectors. Actually, if you would like to print all subsets for 32 elements it would generate terabytes of data. You could use 64-bit integer if you feel limited by constant 32, but you wouldn't even end iterating through all the numbers. If your problem is just answering how many are desired subsets, you definitely need another approach. And STL won't be much helpful also ;-)



回答3:

As you can use any container I would use std::set because it is next to what we want to represent. Now your task is to find all subsets ending with 5 so we take our initial set and remove 5 from it. Now we want to have all subsets of this new set and append 5 to them at the end.

void subsets(std::set<std::set<int>> &sets, std::set<int> initial)
{
    if(initial.empty())
        return;

    sets.insert(initial);//save the current set in the set of sets

    std::set<int>::iterator i = initial.begin();
    for(; i != initial.end(); i++)//for each item in the set
    {
        std::set<int> new_set(initial);//copy the set
        new_set.erase(new_set.find(*i));//remove the current item
        subsets(sets, new_set);//recursion ...
    }
}

sets is a set that contains all subsets you want. initial is the set that you want to have the subsets of. Finally call this with subsets(all_subsets, initial_list_without_5);

This should create the subsets and finally you can append 5 to all of them. Btw don't forget the empty set :)

Also note that creating and erasing all these sets is not very efficient. If you want it faster the final set should get pointers to sets and new_set should be allocated dynamically...



回答4:

use permutation to create a vector of vectors. Then use std::partition with a function to sort it into the vectors that end with 5 and those that don't.