用于分组以解决冲突的算法

Algorithm for grouping to resolve collisions

本文关键字:冲突 算法 解决 用于      更新时间:2023-10-16

来自谷歌的算法问题:

老师想把他的问题学生分成两组。他有一个名字列表(成对),代表不能被归入同一组的学生。我们的任务是检查是否有可能在没有碰撞的情况下将所有学生分开。

例如,如果列表为:

Jack Jim (cannot be in the same group)
Jim Rose (...)
Rose Jack (...)

那么就不可能在没有碰撞的情况下将它们全部分开。

我的想法是使用图的概念,并使用关联数组或映射来实现它。但是,我认为如果图有很多不连接的分支,那将非常复杂。有人可以帮忙吗?

您要检查图形是否为二分图。维基百科有关于如何做到这一点的细节。

这是一个相关的SO问题和一个来自普林斯顿的Java实现。

这听起来像是一个图形着色问题。 首先宣布杰克属于"黑色"组。 这意味着吉姆必须属于"红色"组。 这意味着"玫瑰"必须属于"黑色组"。 现在我们得到了碰撞:因为玫瑰是"黑色",杰克一定是"红色",但我们已经给他分配了黑色。


编辑:用于实现的伪代码(我没有编译它,我知道它会泄漏内存)

enum Group {
    UNKNOWN,
    RED,
    BLACK
};
struct Person
{
    string          name;
    Group           group;
    set<Person*>    exclusionList;
}
class Class
{
    public:
        void addExclusion(const string& inPersonA, const string& inPersonB)
        {
            bool first = (mMembers.empty());
            Person* personA = findPerson(inPersonA);
            Person* personB = findPerson(inPersonB);
            personA->exclusinList.insert(personB);
            personB->exclusionList.insert(personA);
            if (first) {
                // special case, assign initial colors
                personA->color = BLACK;
                personB->color = RED;
            } else {
                switch (personA->color) {
                    case UNKNOWN:
                        switch(personB->color) {
                            case UNKNOWN:
                                break; // we can't do anything, nothing is known
                            case BLACK:
                                setColor(personA, RED);
                                break;
                            case RED:
                                setColor(personA, BLACK);
                                break;
                        }
                        break;
                    case RED:
                        switch (personB->color) {
                            case UNKNOWN:
                                setColor(personB, BLACK);
                                break;
                            case RED:
                                throw "EXCLUSION FAILURE";
                            case BLACK:
                                break;
                       }
                    case BLACK:
                        switch (personB->color) {
                            case UNKNOWN:
                                setColor(personB, BLACK);
                                break;
                            case RED:
                                break;
                            case BLACK:
                                throw "EXCLUSION FAILURE";
                       }
                }
            }
        }
    private:
        Person* findPerson(const string& inString)
        {
            Person* rval = mMembers[inString];
            if (rval == null) {
                rval = new Person(inString, UNKNOWN);
                mMembers[inString] = rval;
            }
            return rval;
        }
        void setColor(Person* inPerson, Group inColor)
        {
            if (inPerson->color == inColor)
               return; // no op
            if (inPerson->color != UNKNOWN && inPerson->color != inColor)
               throw "EXCLUSION FAILURE";
            // now we know inPerson was UNKNOWN
            inPerson->color = inColor;
            for (auto iter = inPerson->exclusionList.begin(); iter != inPerson->exclusionList.end(); ++iter) {
                setColor(*iter, (inColor == RED) ? BLACK : RED);
        }
        unordered_map<string, Person*> mMembers;
};
#algorithm.coffee
#teacher wants to separate his/her problem prisoners into two groups by keeping 
#separated certain individuals. we have a list of kids and need to decide how to 
#separate them according to rules provided.  only two groups allowed apparently. if 
#more are required we are in collision situation.
reset = 'x1B[0m'
red   = 'x1B[0;31m'
green = 'x1B[0;32m'
#we list all the kids, and such that the values are arrays holding all problems associated with that
# key=kid
contras = 
  "abe": []
  "billy": []
  "bella": []
  "charles": []
  "dafner": []
  "echo": []
  "ferris": []
  "gomer": []
  "gina": []
  "heloise": []
#we have empty groups
group1 = []
group2 = []
# defining problem relationships
problems = [
  ["abe", "heloise"]
  ["bella", "dafner"]
  ["gomer", "echo"]
  #["abe", "bella"]
  ["heloise", "dafner"]
  ["echo", "ferris"]
  ["abe", "dafner"]
]
# with the problems array we can populate the contras object
for item in problems
  contras[item[0]].push item[1]
  contras[item[1]].push item[0]
# with the populated contras object we can determine conflicts
for key, value of contras
  for item in value
    for item2 in value
      for item3 in contras[item]
        if item2 == item3 and item3 != item
          console.log red + "There is a collision implicit in problem pair " + reset + key + red + " and " + reset + item + red + " because both " + reset + key + red + " and " + reset + item + red + " are problematic with " + reset + item3 + red + " who is also known as " + reset + item2 + red + ".n"

# if there are no unresolvable conflicts then this routine below
# will work, otherwise you'll see a bunch of
# red flags thrown by the routine above.
for item in problems
  if group1.length == 0
    group1.push item[0]
    group2.push item[1]
  else
    duplicate = false
    for item2 in group1
      if item2 in contras[item[0]] then duplicate = true
    if duplicate == true 
      group1.push item[1] unless item[1] in group1
      group2.push item[0] unless item[0] in group2
    else
      group1.push item[0] unless item[0] in group1
      group2.push item[1] unless item[1] in group2


###  some tests
# checking if group1 contains problems
for item in group1
  for item2 in problems
    for item3 in item2
      if item == item3
        for item4 in item2
          if item4 != item
            for item5 in group1
              if item4 == item5
                duplicate = false
                for item6 in collisions
                  if item2 == item6 then duplicate = true
                if duplicate == false
                  collisions.push item2
# checking if group2 contains problems
for item in group2
  for item2 in problems
    for item3 in item2
      if item == item3
        for item4 in item2
          if item4 != item
            for item5 in group2
              if item4 == item5
                duplicate = false
                for item6 in collisions
                  if item2 == item6 then duplicate = true
                if duplicate == false
                  collisions.push item2
###
console.log green + "whole kids group is " + reset + kids
console.log green + "group1 is " +reset + group1
console.log green + "group2 is " + reset + group2
# console.log red + "collisions are " + reset + collisions

您所需要的只是检查您的图形是否是二分的(即图形的顶点是否能够以这样一种方式分配两种颜色之一,即没有边连接相同颜色的顶点)。如果您使用整数对班级中的学生进行编号:

1. Jack
2. Jim
3. Rose

您可以使用 Boost 图形库轻松解决此问题C++:

#include <iostream>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/bipartite.hpp>
using namespace boost;
int main (int argc, char **argv)
{
    typedef adjacency_list <vecS, vecS, undirectedS> vector_graph_t;
    typedef std::pair <int, int> E;
    E edges[] = { E (1, 2), E (2, 3), E (3, 1)};
    vector_graph_t students (&edges[0],&edges[0] + sizeof(edges) / sizeof(E), 3);
    if ( is_bipartite(students) )
        std::cout << "Bipartite graph" << std::endl;
    else
        std::cout << "Non bipartite graph" << std::endl;
    return 0;
}