来自Google的算法问题:
老师想把他的问题学生分成两组。他有一个名单(成对),代表不能被放入同一组的学生。我们的任务是检查是否可以在没有碰撞的情况下将所有学生分开。
例如,如果列表是:
Jack Jim (cannot be in the same group)
Jim Rose (...)
Rose Jack (...)
然后在没有碰撞的情况下将它们全部分开是不可能的。
我的想法是使用图的概念,并使用关联数组或映射来实现它。但是,我认为如果图中有许多未连接的分支,那将非常复杂。任何人都可以帮忙吗?
答案 0 :(得分:4)
您想要检查图表是否为 bipartite 。 Wikipedia详细介绍了如何操作。
以下是普林斯顿的related SO question和Java implementation。
答案 1 :(得分:1)
这听起来像是一个图形着色问题。首先宣布杰克处于“黑色”组。这意味着吉姆必须处于'红色'组。这意味着“玫瑰”必须属于“黑人组”。现在我们碰到了碰撞:因为玫瑰是'黑色',杰克必须是'红色',但我们已经给他一个黑色。
编辑:用于实现的伪代码(我没有编译它,我知道它泄漏了内存)
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;
};
答案 2 :(得分:0)
#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
答案 3 :(得分:0)
您只需检查图形是否为二分图(即图形的顶点是否能够以这样的方式分配两种颜色中的一种,即没有边连接相同颜色的顶点)。如果您使用整数为班上的学生编号:
1. Jack
2. Jim
3. Rose
您可以使用C ++中的Boost Graph Library轻松解决此问题:
#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;
}