提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
一、图着色问题是什么?
1.图着色问题
图着色问题(Graph Coloring Problem, GCP) 又称着色问题,是最著名的NP-完全问题之一。道路着色问题(Road Coloring Problem)是图论中最著名的猜想之一。给定一个无向图G=(V, E),其中V为顶点集合,E为边集合,图着色问题即为将V分为K个颜色组,每个组形成一个独立集,即其中没有相邻的顶点。其优化版本是希望获得最小的K值。
2.四色原理
地图着色问题与著名四色定理:四色定理是一个著名的数学定理:如果在平面上划出一些邻接的有限区域,那么可以用四种颜色来给这些区域染色,使得每两个邻接区域染的颜色都不一样:另一个通俗简洁的说法是:每个地图都可以用不多于四种颜色来染色,而且不会有两个邻接的区域颜色相同。这就是著名的四色定理,由四色定理可以想到只需要四种颜色就可以为一个区域的地图着上颜色,而且相邻区域的颜色不相同。
二、方法
1.图的表示
计算机中,图主要可以用两种方法表示:邻接矩阵和邻接链表。
地图可以抽象为一个图,可以用邻接矩阵来进行模拟:对于每一个地图,我们可以把每一个城市看作一个点,而城市与城市之间的邻接关系看作点与点之间的连线。从而将地图抽象为一个无向图,从而用邻接矩阵抽象表示。两点邻接,则为1,两点不邻接,则为0。
2.主要思路
用邻接矩阵表示出图,然后采用贪心、递归算法的思想求最小着色数。
(1)具体方法:贪最小着色数,从输入的第一块进行着色,颜色从1到N,每次着色都从颜色1开始尝试,满足要求,就对这一块开始着色,然后进行下一块的着色,不满足要求则使用下一种颜色进行尝试。
关键代码
int colorsame(int s,Graph G)/*判断这个颜色能不能满足要求*/
{
int i,flag=0;
for(i=1;i<=s-1;i++){/*分别与前面已经着色的几块比较*/
if(G.arcs[i][s]==1 && color[i]==color[s]){//只比较相邻的点颜色是否相同
flag=1;//如果邻接的点颜色相同返回1,否则返回0
break;
}
}
return flag;
}
(2)使用递归,进行着色,自己调用自己。
关键代码
void trycolor(int s,Graph G)/*填颜色,s为开始图色的顶点,本算法从1开始*/
{
int i;
if(s>G.vnum){/*如果s大于顶点的个数,递归出口*/
output(G);
exit(1);
}else{
for(i=1;i<=N;i++){/*对每一种色彩逐个测试*/
color[s]=i;
//如果flag=1进行下一种颜色的测试
if(colorsame(s,G)==0){
trycolor(s+1,G);/*进行下一块的着色*/
break;
}
}
}
}
(3)最小着色数即为图形颜色的最大值。因为颜色的名称是从1-N的编写的,求出图形所用颜色的最大数即为最小着色数。
3.优化
元素=[1,2,3,4,5,6,7,8,9] 互斥=[(1,4),(2,5),(1,5),(5,6),(7,8),(3,9),(2,8),(4,5)]
把元素组成 N 个组, 保证互斥元素不在同一个组里, 并且 N 最小
这个问题实际上就是图的m可着色优化问题。
这个图的m可着色优化问题首先不难想到通过二分法(加上0x01中的方法)来做,但是这么做的话时间复杂度过高。可以直接采用dfs来处理。
深度优先搜索也就是回溯法
4.dfs方法
图着色类似问题,参考蓝桥杯往年真题。
题目:
n个人参加某项特殊考试。
为了公平,要求任何两个认识的人不能分在同一个考场。
求是少需要分几个考场才能满足条件。
输入格式
第一行,一个整数n(1<n<100),表示参加考试的人数。
第二行,一个整数m,表示接下来有m行数据
以下m行每行的格式为:两个整数a,b,用空格分开 (1<=a,b<=n) 表示第a个人与第b个人认识。
输出格式
一行一个整数,表示最少分几个考场。
样例输入
5
8
1 2
1 3
1 4
2 3
2 4
2 5
3 4
4 5
样例输出
4
样例输入
5
10
1 2
1 3
1 4
1 5
2 3
2 4
2 5
3 4
3 5
4 5
样例输出
5
解题关键:判断返回条件和回溯
附上代码:
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<map>
using namespace std;
const int INF=0x3f3f3f3f;
const int MAX=105;
int a[MAX][MAX];
int n,m;
//int vis[MAX]; //记录每个人对应哪个房间
int mp[MAX]; //记录每个房间有多少人
int room[MAX][MAX]; //第i个房间第j个人是编号为x的学生
int ans;
void dfs(int x,int num)
{
if(num>=ans) //剪枝
return;
if(x==n+1) //注意是n+1(这样n才被算过)(结束条件)!
{
ans=min(ans,num);
return;
}
for(int i=1;i<=num;i++)
{
int j,l=mp[i];//每个房间有多少人
for(j=1;j<=l;j++)
{
int k=room[i][j];
if(a[x][k]) //同一个房间但有关系
break;
}
if(j==l+1)
{
mp[i]++;
room[i][mp[i]]=x;
dfs(x+1,num);
mp[i]--;//回溯
//room[i][l+1]=0;
}
}
mp[num+1]++;
room[num+1][mp[num+1]]=x;
dfs(x+1,num+1);
mp[num+1]--;//回溯
//room[num+1][1]=0;
}
int main()
{
while(scanf("%d%d",&n,&m)==2)
{
ans=INF;
memset(a,0,sizeof(a));
memset(mp,0,sizeof(mp));
memset(room,0,sizeof(room));
int u,v;
for(int i=0;i<m;i++)
{
scanf("%d%d",&u,&v);
a[u][v]=a[v][u]=1;
}
room[1][1]=1;
mp[1]++;
dfs(2,1);
//dfs(1,0);
printf("%d\n",ans);
}
return 0;
}