It sounds like a graph coloring problem. Start by declaring that Jack is in the 'black' group. This means Jim has to be in the 'red' group. This means 'Rose' must be in the 'black group.' Now we get teh collision: Because rose is 'black,' Jack must be 'Red,' but we already assigned him a black color.
Edit: Pseudocode for implementation (I haven't compiled it, and I know it leaks memory)
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;
};