本文已参与「新人创作礼」活动,一起开启掘金创作之路
图的着色问题
在一个无向图中,相邻的俩个顶点的颜色不可以相同
数学定义:给定一个无向图G=(V, E),其中V为顶点集合,E为边集合,图着色问题即为将V分为K个颜色组,每个组形成一个独立集,即其中没有相邻的顶点。其优化版本是希望获得最小的K值。
问题域
- 图的m-着色判定问题——给定无向连通图G和m种不同的颜色。用这些颜色为图G的各顶点着色,每个顶点着一种颜色,是否有一种着色法使G中任意相邻的2个顶点着不同颜色?
- 图的m-着色优化问题——若一个图最少需要m种颜色才能使图中任意相邻的2个顶点着不同颜色,则称这个数m为该图的色数。求一个图的最小色数m的问题称为m-着色优化问题。
解决方案
1.图的m-着色判定问题
用户输入格式:输入顶点数、颜色数、和边数,还有无向图中有有相连边的顶点。
输入顶点数、颜色数和边数均为整数,若输入不符合格式,则提示用户重新输入。
主函数main代码如下:
n为顶点数,m为图的颜色数,edge为图的边数,以及核心函数graphcolor
int main()
{
int n, m, edge;
int i, j;
//输入顶点数为整数,否则一直循环
printf("----------图的顶点数、颜色和边数最大为200----------\n");
while (1)
{
printf("请输入图的顶点数:\n");
int num = scanf("%d", &n);
if (num < 0)
{
printf("Warning!<请输入格式正确的整数>\n");
}
if (n < 0 || n > 200)
{
printf("Warning!<请输入大于等于0且小于200的整数>\n");
}
else
break;
cin.sync();
}
//输入图的颜色数为整数,否则一直循环
while (1)
{
printf("请输入图的颜色数:\n");
int num = scanf("%d", &m);
if (num < 0)
{
printf("Warning!<请输入格式正确的整数>\n");
}
if (m < 0 || m > 200)
{
printf("Warning!<请输入大于等于0且小于200的整数>\n");
}
else
break;
cin.sync();
}
//输入无向图的边数为整数,否则一直循环
while (1)
{
printf("请输入图的边数:\n");
int num = scanf("%d", &edge);
if (num < 0)
{
printf("Warning!<请输入格式正确的整数>\n");
}
if (edge < 0 || edge > 200)
{
printf("Warning!<请输入大于等于0且小于200的整数>\n");
}
else
break;
cin.sync();
}
(edge > 0) ? printf("%请输入邻接矩阵,即输入有边相连的俩个顶点,如顶点1和顶点2有相连的边,则输入1 2:\n") : 1;
for (int k = 0; k < edge; k++)
{
while (1)
{
int num = scanf("%d%d", &i, &j);
if(num < 2)
printf("请输入正确格式的整数\n");
else if(i<=0 || i >200 || j <=0 || j>200)
printf("请输入规定范围内的整数\n");
else
break;
cin.sync();
}
edges[j][i] = edges[i][j] = 1;
}
(graphcolor(n, m)) ? 1 : printf("该情况下无解");
return 0;
}
核心函数graphcolor代码如下
bool graphcolor(int n, int m)
{
bool flag = false;
int k = 1;
int count = 0;
while (k >= 1)
{
color[k] = color[k] + 1;
while (color[k] <= m)
if (judge(k))
{
break;
}
else
{
color[k] = color[k] + 1;
}
if (color[k] <= m && k == n)
{
flag = true;
count++;
printf("--%d种解法为--\n", count);
for (int i = 1; i <= n; i++)
{
printf("%d--%d\n", i, color[i]);
}
printf("-------------\n");
}
else if (color[k] <= m && k < n)
{
k++;
}
else
{
color[k] = 0;
k--;
}
}
return flag;
}
判断是否出现相邻顶点颜色一样的函数judge代码如下:
bool judge(int k)
{
for (int i = 1; i < k; i++)
{
if (edges[k][i] == 1 && color[i] == color[k])
return false;
}
return true;
}
现在解释大概思路,graphcolor函数形参n,m分别代表顶点数和边数
设置一个涂色方案数组color,数组中初始值全为0,flag变量记录是否有解,如果有解,则函数返回true,无解函数返回false,变量k用来控制涂色的顶点,变量count用来记录解法。
从第一个顶点1开始,陆续给顶点安排颜色号,每一个顶点均从1开始,若涂上当前颜色号judge返回false,则安排下一个颜色号,重新判断,直到judge返回true,也就是无相邻边的颜色相同。
(1)当当前顶点为涂完色的顶点,且顶点号仍在范围内的时候,有解法,输出顶点涂的颜色号。
(2)当当前顶点不是最后一个涂完色的顶点,即还存在未涂颜色的顶点时,k++,即进行下一个顶点的涂色工作。
(3)如果不是上述俩种情况,则剩余的情况为1)若当前顶点涂的颜色超过规定颜色数,这个时候需要将该顶点颜色号置为0,并回溯到上一个顶点,让上一个顶点的颜色号找到下一个可以涂的颜色,再次判断当前顶点是否能有解,如果上一个顶点无法找到让当前顶点有解的解法,继续回溯,若回溯到第一个顶点,仍没有有解的解法,则当前情况无解。2)当前顶点为最后一个顶点,已经输出完最后一个顶点能涂颜色的解数,需要回溯到上一个顶点,让上一个顶点找到下一个能涂的颜色,继续输出其他符合条件的解。
\