1、TSP问题
2、图着色问题
3、最小生成树问题
1、TSP问题
(1)最近邻点策略:从任意城市出发,每次在没有到过的城市中选择最近的一个,直到经过了所有的城市,最后回到出发城市。
设图G有n个顶点,边上的代价存储在二维数组w[n][n]中,集合V存储图的顶点,集合P存储经过的边,最近邻点策略求解TSP问题的算法如下:
int n;//顶点的个数
//int arc[100][100]; //各边的代价
int TSP1(int arc[][], int w) { //假设从顶点w出发
int endgCount = 0, TSPLength = 0;
int min, u, v;
int flag[] = { 0 }; //顶点均未加入哈密顿回路
u = w;
flag[w] = 1;
while (endgCount < n - 1) { //循环到边数等于n-1
min = 100;
for (int j = 0; j < n; j++) { //求arc[u]的最小值
if ((flag[j] == 0) && (arc[u][j] != 0) && (arc[u][j] < min)) {
v = j;
min = arc[u][j];
}
}
TSPLength += arc[u][v];
flag[v] = 1; //表示走过了,将顶点加入哈密顿回路
endgCount++;
cout << u << "-->" << v << endl; //输出经过的路径
u = v; //下一次从顶点v出发
}
cout << v << "-->" << w << endl; //输出最后的回边
return (TSPLength + arc[u][w]); //返回最短哈密顿回路的长度
}
(2)最短链接策略:每次在整个图的范围内选择最短边加入到解集合中,但是,要保证加入解集合中的边最终形成一个哈密顿回路。因此,当从剩余边集E’中选择一条边(u, v)加入解集合S中,应满足以下条件:
① 边(u, v)是边集E’中代价最小的边;
② 边(u, v)加入解集合S后,S中不产生回路;
③ 边(u, v) 加入解集合S后,S中不产生分枝;
设图G有n个顶点,边上的代价存储在二维数组w[n][n]中,集合E’是候选集合即存储所有未选取的边,集合P存储经过的边,最短链接策略求解TSP问题的算法如下:
2、图着色问题
给定无向连通图G=(V, E),求图G的最小色数k,使得用k种颜色对G中的顶点着色,可使任意两个相邻顶点着色不同。
贪心策略:
选择一种颜色,以任意顶点作为开始顶点,依次考察图中的未被着色的每个顶点,如果一个顶点可以用颜色1着色,换言之,该顶点的邻接点都还未被着色,则用颜色1为该顶点着色;
当没有顶点能以这种颜色着色时,选择颜色2和一个未被着色的顶点作为开始顶点,用第二种颜色为尽可能多的顶点着色,
如果还有未着色的顶点,则选取颜色3并为尽可能多的顶点着色,依此类推。
设数组color[n]表示顶点的着色情况,贪心法求解图着色问题的算法如下:
int n;
int color[100];//表示各顶点的着色情况
int arc[100][100];//图中各边的代价
void ColorGraph() {
int k = 0;
int flag = 1; //图中未着色的顶点为1
while (flag == 1) {
k++; //取下一种颜色
flag = 0;
for (int i = 0; i < n; i++) {
if (color[i] == 0) {
color[i] = k; //顶点i涂上颜色k
if (!Ok(i)) { //发生冲突,取消涂色
color[i] = 0;
flag = 1;
}
}
}
}
}
int Ok(int i) { //判断顶点i的着色是否发生冲突
for (int j = 0; j < n; j++) {
if (arc[i][j] == 1 && color[i] == color[j]) { //两点中间有连线,并且颜色相同
return 0;
}
else {
return 1;
}
}
}
3、最小生成树问题
(1)最近顶点策略:任选一个顶点,并以此建立起生成树,每一步的贪心选择是简单地把不在生成树中的最近顶点添加到生成树中。
Prim算法就应用了这个贪心策略,它使生成树以一种自然的方式生长,即从任意顶点开始,每一步为这棵树添加一个分枝,直到生成树中包含全部顶点。
Prim算法
设连通网中有n个顶点,则第一个进行初始化的循环语句需要执行n-1次,第二个循环共执行n-1次,内嵌两个循环,其一是在长度为n的数组中求最小值,需要执行n-1次,其二是调整辅助数组,需要执行n-1次,所以,Prim算法的时间复杂度为O(n2)。
(2)最短边策略:设G=(V,E)是一个无向连通网,令T=(U,TE)是G的最小生成树。最短边策略从TE={}开始,每一次贪心选择都是在边集E中选取最短边(u, v),如果边(u, v)加入集合TE中不产生回路,则将边(u, v)加入边集TE中,并将它在集合E中删去。
Kruskal算法就应用了这个贪心策略,它使生成树以一种随意的方式生长,先让森林中的树木随意生长,每生长一次就将两棵树合并,到最后合并成一棵树。
Kruskal算法
Kruskal算法为了提高每次贪心选择时查找最短边的效率,可以先将图G中的边按代价从小到大排序,则这个操作的时间复杂度为O(elog2e),其中e为无向连通网中边的个数。对于两个顶点是否属于同一个连通分量,可以用并查集的操作将其时间性能提高到O(n),所以,Kruskal算法的时间性能是O(elog2e)。