Search code examples
c++stdliststdset

Task with inserting elements to list - tough a little


This is just a continuation of several past questions.

My function should return std::vector<std::set<std::string>>

A group of names should be classified into teams for a game. Teams should be the same size, but this is not always possible unless n is exactly divisible by k. Therefore, they decided that the first mode (n, k) teams have n / k + 1 members, and the remaining teams have n / k members.

Vector of strings which function accepts must be converted to list of strings.

I need to move through list elements and add them to the set. However, while moving through list I need to skip elements from list that are already added to set. First element of list is added to set before moving through list.

Moving is calculated based on length of last inserted member. For example if Damir is inserted, then move (shift) will be 5.

EXPLANATION:

list

UPDATE [after @stefaanv's suggestion]:

#include <iostream>
#include <list>
#include <set>
#include <string>
#include <vector>
typedef std::vector<std::set<std::string>> vek;
vek Distribution(const std::vector<std::string>&names, int k) {
  vek teams(k);
  int num = names.size();
  int number_of_first = num % k;
  int number_of_members_first = num / k + 1;
  int number_of_members_remaining = num / k;
  std::list<std::string> lista;
  for (int i = 0; i < num; i++)
    lista.push_back(names[i]);
  auto it = lista.begin();
  auto temp = it;
  int n = num, new_member = 0, index_list = 0;
  for (int i = 0; i < k; i++) {
    if (i <= number_of_first) {
      int number_of_members_in_team = 0;
      while (number_of_members_in_team < number_of_members_first) {
        for (int i = 0; i < new_member; i++)
          index_list++;
        if (index_list > n - 1)
          index_list = index_list - n;
        while (it != lista.begin())
          it--;
        for (int i = 0; i < index_list; i++)
          it++;
        teams[i].insert(*it);
        number_of_members_in_team++;
        new_member = it->length();
        it = lista.erase(it);
        n--;
      }
    } else {
      int number_of_members_in_team = 0;
      while (number_of_members_in_team < number_of_members_remaining) {
        for (int i = 0; i < new_member; i++)
          index_list++;
        if (index_list > n - 1)
          index_list = index_list - n;
        while (it != lista.begin())
          it--;
        for (int i = 0; i < index_list; i++)
          it++;
        teams[i].insert(*it);
        number_of_members_in_team++;
        new_member = it->length();
        it = lista.erase(it);
        n--;
      }
    }
  }
  return teams;
}
int main() {
  for (auto i :
       Distribution({"Damir", "Ana", "Muhamed", "Marko", "Ivan", "Mirsad",
                     "Nikolina", "Alen", "Jasmina", "Merima"},
                    3)) {
    for (auto j : i)
      std::cout << j << " ";
    std::cout << std::endl;
  }
  return 0;
}

Correct output would be:

Ana Damir Mirsad Muhammed

Ivan Merima Nikolina

Alen Jasmina Marko

I get nothing in the output!

I just need simple fix in algorithm. Reason why I have nothing in output is this:

it = lista.erase(it);
n--;

If I don't use those lines of code I would get 3 teams with 4, 3, and 3 names respectively, just with wrong names. So now I get the right number of teams and the right number of team members. The only problem here remains to add correct names to teams...

Could you give me better approach?


Solution

  • One wrong thing in your check function: you only check the names in the current team to skip that, but you should skip any name that was already put in a team.

    I already gave you an alternative on another question, so the check function isn't needed (erasing from the list).

    Also, when iterating over a list, you should take into account that when you reach end, you must continue at begin. Adding the names a 100 times isn't a good alternative.

    Hints:

    • try to put in comments what your code is supposed to do so you and we can see why the code doesn't work as expected.
    • When possible, use const reference to pass data-structures to functions
    • For the number of next shifts: it->length() works too, no need to look for the name.
    • You can easily avoid the code duplication, the only difference is one number.
    • i and l have the same meaning and when you iterate i from 0 to < k, they become the same.

    EDIT after changing code in the question based on my comments

    Code how I would do it based on your edited code (there are two names switched with the expected output), make_team() and Distribution() are working together on the data, so they could be joined in a class:

    #include <iostream>
    #include <list>
    #include <set>
    #include <string>
    #include <vector>
    
    // data structure for result
    typedef std::vector<std::set<std::string>> ResultType;
    
    // put players in one team according to shift algorithm (shifts is length of name of last player) to make more random
    // list_players and it_players are references and are being changed in this function
    std::set<std::string> make_team(std::list<std::string>& list_players, std::list<std::string>::iterator& it_players,int size)
    {
      std::set<std::string> team;
      int number_shifts = 0;
      int number_of_members_in_team = 0;
      while (number_of_members_in_team < size) 
      {
        // shift to new selection (rotate: end becomes begin)
        for (int i = 0; i < number_shifts - 1; i++)
        {
          it_players++;
          if (it_players == list_players.end()) it_players = list_players.begin();
        }
        // move to team by inserting and deleting from list
        team.insert(*it_players);
        number_of_members_in_team++;
        number_shifts = it_players->length(); // distribute algorithm: shift number of times as length of name of last chosen player
        it_players = list_players.erase(it_players);
        if (list_players.empty()) // just in case
          return team;
      }
      return team;
    }
    
    // distribute players to given number of teams
    ResultType Distribution(const std::vector<std::string>&names, int number_teams) {
      // init
      ResultType teams(number_teams);
      int number_players = names.size();
      int number_of_first = number_players % number_teams;
      int number_of_members = number_players / number_teams + 1; // adjusted after number_of_first
      std::list<std::string> list_players(names.begin(), names.end());
      auto it_players = list_players.begin();
      
      // do for all teams
      for (int tm = 0; tm < number_teams; ++tm) 
      {
        if (tm == number_of_first) // adjust because not all teams can have the same size, so first teams can be larger by 1
          number_of_members--;
    
        teams[tm] = make_team(list_players, it_players, number_of_members);
        if (list_players.empty())
          return teams;
      }
      return teams;
    }
    
    // test harnass
    int main() {
      for (auto i :
           Distribution({"Damir", "Ana", "Muhamed", "Marko", "Ivan", "Mirsad",
                         "Nikolina", "Alen", "Jasmina", "Merima"},
                        3)) {
        for (auto j : i)
          std::cout << j << " ";
        std::cout << std::endl;
      }
      return 0;
    }