【发布时间】:2018-06-02 21:44:33
【问题描述】:
考虑一个有 V 个顶点和 E 个边的图 G(V,E)。我们想用 K 种颜色给顶点图上色。
着色图意味着以两个相邻顶点不应具有相同颜色的方式为每个节点分配颜色。
我们如何实现这个问题?
【问题讨论】:
标签: algorithm graph graph-algorithm graph-coloring
考虑一个有 V 个顶点和 E 个边的图 G(V,E)。我们想用 K 种颜色给顶点图上色。
着色图意味着以两个相邻顶点不应具有相同颜色的方式为每个节点分配颜色。
我们如何实现这个问题?
【问题讨论】:
标签: algorithm graph graph-algorithm graph-coloring
首先让我们注意 2 假设:
我们可以使用贪心算法来解决这个问题。
让每个颜色分配编号 [1,2,...,k] - 让我们用 Ci 表示颜色 i。从任意节点 v1 开始并分配给他 C1。现在让在每个节点上的图表上运行 BSF,选择他的调整节点中不存在的最小颜色 - 如果另一个节点没有颜色但忽略它们。如果 d(v) > k 并且他的所有调整都是不同的颜色,则返回 false。
伪代码:
// v.color init as 0 for all V
Queue <- new Queue(v1)
While Queue not empty:
current <- Queue.pop
if (current.color != 0 )
continue
adjs <- current.getAdj()
colors = new Set
for each adjs as adj:
colors.add(adj.colors)
for i = 1 to k:
if i not in colors: //found lowest color avilable
current.color <- C[i]
break
if current.color == 0 return false // cannot assign any color
Queue.insert(adjs)
【讨论】:
维基百科中graph coloring algorithms 上的条目指出,图形是否允许正确的(= 如果通过边连接,则没有两个相同颜色的顶点)着色与完全 k 种颜色的问题是NP完全。
蛮力算法是您所希望的最好算法(除非您有其他约束,例如图是二分图或平面图)。蛮力算法如下:
#include <iostream>
#include <string>
using namespace std;
// describes a partial answer to the problem
struct Coloring {
static const int maxV = 5;
// only the first k colors count
int colors[maxV];
void show(int k) const {
cout << "{";
for (int i=0; i<k; i++) {
cout << colors[i] << " ";
}
cout << "}" << endl;
}
};
// A graph
struct Graph {
int availableColors;
int numV;
bool edges[Coloring::maxV][Coloring::maxV];
void handleAnswer(Coloring &s) const {
cout << "EUREKA: ";
s.show(numV);
}
// checks if the k-th vertex avoids being same-color as neighbors
bool isPartialAnswer(const Coloring &s, int k) const {
cout << std::string(k, ' ') << "testing: ";
s.show(k);
for (int i=0; i<k; i++) {
for (int j=0; j<i; j++) {
if ((edges[i][j] || edges[j][i])
&& (s.colors[i] == s.colors[j])) {
cout << std::string(k, ' ') << " .. but "
<< i << " & " << j << " have same color" << endl;
return false;
}
}
}
return true;
}
bool isAnswer(const Coloring &s, int k) const {
return k == numV;
}
};
void paint(Coloring &s, int k, const Graph &c) {
// initializes level
cout << std::string(k, ' ') << "entering k=" << k << ": ";
s.show(k);
// test with each possible color for the next vertex
for (int i=0; i<c.availableColors; i++) {
// modify current partial answer
s.colors[k] = i;
// is it still a partial answer?
if (c.isPartialAnswer(s, k+1)) {
// is it a full answer?
if (c.isAnswer(s, k+1)) {
c.handleAnswer(s);
} else {
// continue down this road
paint(s, k+1, c);
}
}
}
// backtrack: we have exhausted all continuations of this coloring
cout << std::string(k, ' ') << "closing k=" << k << endl;
}
int main() {
Graph c = {4, 4,
{{0, 1, 0, 0, 0},
{0, 0, 1, 1, 0},
{0, 0, 0, 1, 0},
{0, 0, 0, 0, 0},
{0, 0, 0, 0, 0},}};
Coloring s;
paint(s, 0, c);
return 0;
}
免责声明:这是回溯算法的典型示例,其设计目的更多是为了清晰而不是性能或可扩展性。
【讨论】: