## 5.5 Graph Coloring
The *m*-Coloring problem concerns finding all ways to color an undirected graph using at most *m* different colors, so that no two adjacent vertices are the same color. We usually call the *m*-Coloring problem a unique problem for each value of *m.*
Example 5.5**![Start example](https://box.kancloud.cn/e95df9d604ab2d89febe370ae4d88fb1_1x1.gif)**Consider the graph in [Figure 5.10](#ch05fig10). There is no solution to the 2-Coloring problem for this graph because, if we can use at most two different colors, there is no way to color the vertices so that no adjacent vertices are the same color. One solution to the 3-Coloring problem for this graph is as follows:
*Vertex*
*Color*
*v*1
color 1
*v*2
color 2
*v*3
color 3
*v*4
color 2
[![Click To expand](https://box.kancloud.cn/b9703ca876f2d39cab4d4ead6bf1b48d_309x229.jpg)](fig5-10_0.jpg)
Figure 5.10: Graph for which there is no solution to the 2-Coloring problem. A solution to the 3-Coloring problem for this graph is shown in [Example 5.5](#ch05ex09).
There are a total of six solutions to the 3-Coloring problem for this graph. However, the six solutions are only different in the way the colors are permuted. For example, another solution is to color *v*1 color 2, *v*2 and *v*4 color 1, and *v*3 color 3.
**![End example](https://box.kancloud.cn/e95df9d604ab2d89febe370ae4d88fb1_1x1.gif)**
An important application of graph coloring is the coloring of maps. A graph is called ***planar*** if it can be drawn in a plane in such a way that no two edges cross each other. The graph at the bottom of [Figure 5.11](#ch05fig11) is planar. However, if we were to add the edges (*v*1, *v*5) and (*v*2, *v*4) it would no longer be planar. To every map there corresponds a planar graph. Each region in the map is represented by a vertex. If one region is adjacent to another region, we join their corresponding vertices by an edge. [Figure 5.11](#ch05fig11) shows a map at the top and its planar graph representation at the bottom. The *m*-Coloring problem for planar graphs is to determine how many ways the map can be colored, using at most *m* colors, so that no two adjacent regions are the same color.
[![Click To expand](https://box.kancloud.cn/561708dd3a7df3be8d23d6567d8e5f53_265x500.jpg)](fig5-11_0.jpg)
Figure 5.11: Map (top) and its planar graph representation (bottom).
A straightforward state space tree for the *m*-Coloring problem is one in which each possible color is tried for vertex *v*1 at level 1, each possible color is tried for vertex *v*2 at level 2, and so on until each possible color has been tried for vertex *vn* at level *n*. Each path from the root to a leaf is a candidate solution. We check whether a candidate solution is a solution by determining whether any two adjacent vertices are the same color. To avoid confusion, remember in the following discussion that "node" refers to a node in the state space tree and "vertex" refers to a vertex in the graph being colored.
We can backtrack in this problem because a node is nonpromising if a vertex that is adjacent to the vertex being colored at the node has already been colored the color that is being used at the node. [Figure 5.12](#ch05fig12) shows a portion of the pruned state space tree that results when this backtracking strategy is applied to a 3-coloring of the graph in [Figure 5.10](#ch05fig10). The number in a node is the number of the color used on the vertex being colored at the node. The first solution is found at the shaded node. Nonpromising nodes are labeled with crosses. After *v*1 is colored color 1, choosing color 1 for *v*2 is nonpromising because *v*1 is adjacent to *v*2. Similarly, after *v*1, *v*2, and *v*3 have been colored colors 1, 2, and 3, respectively, choosing color 1 for *v*4 is nonpromising because *v*1 is adjacent to *v*4.
[![Click To expand](https://box.kancloud.cn/7b68bde400441d6ac4fa9fabe0c7731b_350x287.jpg)](fig5-12_0.jpg)
Figure 5.12: A portion of the pruned state space tree produced using backtracking to do a 3-coloring of the graph in [Figure 5.10](#ch05fig10). The first solution is found at the shaded node. Each nonpromising node is marked with a cross.
Next we present an algorithm that solves the *m*-Coloring problem for all values of *m*. In this algorithm the graph is represented by an adjacency matrix, as was done in [Section 4.1](LiB0033.html#366). However, because the graph is unsheathed, eachentry in the matrix is simply true or false depending on whether or not there is an edge between the two vertices.
Algorithm 5.5: The Backtracking Algorithm for the, m-Coloring Problem**![Start example](https://box.kancloud.cn/e95df9d604ab2d89febe370ae4d88fb1_1x1.gif)**Problem: Determine all ways in which the vertices in an undirected graph can be colored, using only *m* colors, so that adjacent vertices are not the same color.
Inputs: positive integers *n* and *m*, and an undirected graph containing *n* vertices. The graph is represented by a two-dimensional array *W*, which has both its rows and columns indexed from 1 to *n*, where *W \[i\] \[j\]* is true if there is an edge between *i*th vertex and the *j*th vertex and false otherwise.
Outputs: all possible colorings of the graph, using at most *m* colors, so that no two adjacent vertices are the same color. The output for each coloring is an array *vcolor* indexed from 1 to *n*, where *vcolor \[i\]* is the color (an integer between 1 and *m*) assigned to the *i*th vertex.
```
void m_coloring (index i)
{
int color;
if (promising (i))
if (i == n)
cout << vcolor [1] through vcolor [n];
else
for (color = 1; color <= m; color++){ // Try every
vcolor [i + 1] = color; // color for
m_coloring (i + 1); // next vertex.
}
}
bool promising (index i)
{
index j;
bool switch;
switch = true;
j = 1;
while (j && switch){ // Check if an
if (W[i][j] && vcolor[i] == vcolor[j]) // adjacent vertex
switch = false; // is already
j++; // this color.
}
return switch;
}
```
**![End example](https://box.kancloud.cn/e95df9d604ab2d89febe370ae4d88fb1_1x1.gif)**
Following our usual convention, *n*, *m*, *W*, and *vcolor* are not inputs to either routine. In an implementation of the algorithm, the routines would be defined locally in a simple procedure that had *n*, *m*, and *W* as inputs, and *vcolor* defined locally. The top level call to *m*\_coloring would be
![](https://box.kancloud.cn/51d9092cdc6665a2d064a7de123b3087_146x20.jpg)
The number of nodes in the state space tree for this algorithm is equal to
![](https://box.kancloud.cn/37c66ce9200aaeb4afeae431fa432217_290x41.jpg)
This equality is obtained in [Example A.4](LiB0095.html#1299) in [Appendix A](LiB0093.html#1281). For a given *m* and *n*, it is possible to create an instance that checks at least an exponentially large number of nodes (in terms of *n*). For example, if *m* is only 2, and we take a graph in which *vn* has an edge to every other node, and the only other edge is one between *v**n*–2 and *v**n*–1, then no solution exists, but almost every node in the state space tree will be visited to determine this. As with any backtracking algorithm, the algorithm can be efficient for a particular large instance. The Monte Carlo technique described in [Section 5.3](LiB0041.html#500) is applicable to this algorithm, which means that it can be used to estimate the efficiency for a particular instance.
In the exercises, you are asked to solve the 2-Coloring problem with an algorithm whose worst-case time complexity is not exponential in *n*. For *m* ≥ 3, no one has ever developed an algorithm that is efficient in the worst case. Like the Sum-of-Subsets problem and the 0–1 Knapsack problem, the *m*-Coloring problem for *m* ≥ 3 is in the class of problems discussed in [Chapter 9](LiB0074.html#880). This is the case even if we are looking for only one *m*-coloring of the graph.
- Table of Contents
- BackCover
- Foundations of Algorithms Using C++ Pseudocode, Third Edition
- Preface
- Chapter Contents
- Pedagogy
- Course Outlines
- Acknowledgments
- Errors
- Chapter 1: Algorithms - Efficiency, Analysis, and Order
- 1.2 The Importance of Developing Efficient Algorithms
- 1.3 Analysis of Algorithms
- 1.4 Order
- 1.5 Outline of This Book
- Exercises
- Chapter 2: Divide-and-Conquer
- 2.1 Binary Search
- 2.2 Mergesort
- 2.3 The Divide-and-Conquer Approach
- 2.4 Quicksort (Partition Exchange Sort)
- 2.5 Strassen's Matrix Multiplication Algorithm
- 2.6 Arithmetic with Large Integers
- 2.7 Determining Thresholds
- 2.8 When Not to Use Divide-and-Conquer
- Exercises
- Chapter 3: Dynamic Programming
- 3.1 The Binomial Coefficient
- 3.2 Floyd's Algorithm for Shortest Paths
- 3.3 Dynamic Programming and Optimization Problems
- 3.4 Chained Matrix Multiplication
- 3.5 Optimal Binary Search Trees
- 3.6 The Traveling Salesperson Problem
- Exercises
- Chapter 4: The Greedy Approach
- 4.1 Minimum Spanning Trees
- 4.2 Dijkstra's Algorithm for Single-Source Shortest Paths
- 4.3 Scheduling
- 4.4 Huffman Code
- 4.5 The Greedy Approach versus Dynamic Programming: The Knapsack Problem
- Exercises
- Chapter 5: Backtracking
- 5.2 The n-Queens Problem
- 5.3 Using a Monte Carlo Algorithm to Estimate the Efficiency of a Backtracking Algorithm
- 5.4 The Sum-of-Subsets Problem
- 5.5 Graph Coloring
- 5.6 The Hamiltonian Circuits Problem
- 5.7 The 0-1 Knapsack Problem
- Exercises
- Chapter 6: Branch-and-Bound
- 6.1 Illustrating Branch-and-Bound with the 0 - 1 Knapsack problem
- 6.2 The Traveling Salesperson Problem
- 6.3 Abductive Inference (Diagnosis)
- Exercises
- Chapter 7: Introduction to Computational Complexity - The Sorting Problem
- 7.2 Insertion Sort and Selection Sort
- 7.3 Lower Bounds for Algorithms that Remove at Most One Inversion per Comparison
- 7.4 Mergesort Revisited
- 7.5 Quicksort Revisited
- 7.6 Heapsort
- 7.6.1 Heaps and Basic Heap Routines
- 7.6.2 An Implementation of Heapsort
- 7.7 Comparison of Mergesort, Quicksort, and Heapsort
- 7.8 Lower Bounds for Sorting Only by Comparison of Keys
- 7.8.1 Decision Trees for Sorting Algorithms
- 7.8.2 Lower Bounds for Worst-Case Behavior
- 7.8.3 Lower Bounds for Average-Case Behavior
- 7.9 Sorting by Distribution (Radix Sort)
- Exercises
- Chapter 8: More Computational Complexity - The Searching Problem
- 8.1 Lower Bounds for Searching Only by Comparisons of Keys
- 8.2 Interpolation Search
- 8.3 Searching in Trees
- 8.4 Hashing
- 8.5 The Selection Problem: Introduction to Adversary Arguments
- Exercises
- Chapter 9: Computational Complexity and Interactability - An Introduction to the Theory of NP
- 9.2 Input Size Revisited
- 9.3 The Three General Problem Categories
- 9.4 The Theory of NP
- 9.5 Handling NP-Hard Problems
- Exercises
- Chapter 10: Number-Theoretic Algorithms
- 10.1 Number Theory Review
- 10.2 Computing the Greatest Common Divisor
- 10.3 Modular Arithmetic Review
- 10.4 Solving Modular Linear Equations
- 10.5 Computing Modular Powers
- 10.6 Finding Large Prime Numbers
- 10.7 The RSA Public-Key Cryptosystem
- Exercises
- Chapter 11: Introduction to Parallel Algorithms
- 11.1 Parallel Architectures
- 11.2 The PRAM Model
- Exercises
- Appendix A: Review of Necessary Mathematics
- A.2 Functions
- A.3 Mathematical Induction
- A.4 Theorems and Lemmas
- A.5 Logarithms
- A.6 Sets
- A.7 Permutations and Combinations
- A.8 Probability
- Exercises
- Appendix B: Solving Recurrence Equations - With Applications to Analysis of Recursive Algorithms
- B.2 Solving Recurrences Using the Characteristic Equation
- B.3 Solving Recurrences by Substitution
- B.4 Extending Results for n, a Power of a Positive Constant b, to n in General
- B.5 Proofs of Theorems
- Exercises
- Appendix C: Data Structures for Disjoint Sets
- References
- Index
- Index_B
- Index_C
- Index_D
- Index_E
- Index_F
- Index_G
- Index_H
- Index_I
- Index_J
- Index_K
- Index_L
- Index_M
- Index_N
- Index_O
- Index_P
- Index_Q
- Index_R
- Index_S
- Index_T
- Index_U
- Index_V
- Index_W-X
- Index_Y
- Index_Z
- List of Figures
- List of Tables
- List of Algorithms, Examples, and Theorems
- List of Sidebars