问题描述:
给出5个顶点的无向连通图的四着色问题。思路:和八皇后问题雷同,用回溯法逐步试探。先给任一个节点涂色,然后为相邻节点涂色,涂色时逐步试探使得不撞色。
代码如下:
#include
void visit(int g[],int n);
int canDraw(int c[][6],int n, int g[], int k)
{
int i = 1,flag = 1;//flag初始值为0的话,如果k=1,即给第一个节点涂色,下面的for循环都不满足,直接返回flag,为假,这就错了。
for(; i < k; i++)//涂第k个节点时,只要前k-1个已经涂好的节点如果有与这个节点相连颜色不同即可
{
if(c[i][k])
{
if(g[i] != g[k])
flag++;
else
{
flag = 0;
break;
}
}
}
return flag;
}
int drawColor(int c[][6], int n, int g[])
{
int k = 2,find = 0,count = 0, m = 4;
g[1] = 1;//红橙黄绿用1234表示。初始节点(用1表示)涂红色。
find = 1;
while(k >= 1)//当k回退到k=0,跳出循环。如果k>=0,则最后一次回退k=0,又从g[0]=1开始,从第一个节点(k=1)开始涂色有48,回退一次回到 g[0]=2,从第一个节点开始涂色也有48,直到g[0] =5跳出循环,执行回退k=-1 退出程序,如图一所示
{
printf("\nwhile");
find = 0;
//while((find == 0) && (g[k] < 5))//为当前节点k找一个合适颜色,或者设标志位跳出循环,或者如下方式设立break
while((g[k] < 5)) //须有跳出while的break设置
{
g[k]++;
printf("*whilewhile g[%d]=%d ",k,g[k]);
if(canDraw(c,n,g,k)&& (g[k] < 5))//“&& (g[k] < 5)”非常重要。否则g[k]等于5时,会漏掉。见图二和图三。
{
find = 1;
break;//跳出循环
}
}
if(find == 1)//当前节点k可以涂色,继续涂下一个节点
{
if(k == (n-1))//数组长度为n=6,但是节点数为5。
{
printf("##final graph coloring: ");
visit(g,k);
//return 1;//如果只要给出第一种涂色方案,那么直接返回程序就行。要统计所有涂色方案则程序继续执行,一直执行外层的while循环,直到第一个节点试完最后一种颜色,其他节点也没有别的颜色可搭配了为止(此时k=0),程序结束。
count++;
}
else
{
k++;
printf("##next k=%d",k);
}
}
else //当前节点k涂所有颜色终和相邻节点撞色,没法继续涂色,前面一个节点k-1必须换颜色重新涂
{
g[k] = 0;
k--;
printf("*elsebacktrace k=%d",k);
}
}
printf("\nways of drawing graph:count= %d",count);
}
void visit(int g[],int n)//输出涂色方案
{ int i;
for(i = 1 ; i<= n; i++)
{
printf("%2d",g[i]);
}
}
int main()
{
int i,g[6] = {0},c[6][6]={{0,0,0,0,0,0},
{0,0,1,1,1,0},
{0,1,0,1,1,1},
{0,1,1,0,1,0},
{0,1,1,1,0,1},
{0,0,1,0,1,0}};//图的邻接矩阵,表示5个节点邻接关系。设长度为6是因为数组从下标为1开始。
drawColor(c,6, g);
return 1;
}
代码中的图如下所示;
图一
这个图表示,求给出所有涂色方案时,外层while跳出循环的k取值。当k>=0时,出错情况。
图二
图三
图二表示,漏掉判断条件“&& (g[k] < 5)”出错情况,作为对比给出图三的正确情况。