笔者接着上一次的博客继续讨论TSP问题(TSP问题,动态规划法),这次采用贪心法,至少有两种贪心策略是合理的:最近邻点策略和最短链接策略。
(一)最近邻点策略
从任意城市出发,每次在没有到过的城市中选择最近的一个,直到经过了所有的城市,最后回到出发城市。
设图G有n个顶点,边上的代价存储在二维数组w[n][n]中,集合V存储图的顶点,集合P存储经过的边,最近邻点策略求解TSP问题的算法如下:
1. P={ };
2. V=V-{u0}; u=u0; //从顶点u0出发
3. 循环直到集合P中包含n-1条边
3.1 查找与顶点u邻接的最小代价边(u, v)并且v属于集合V;
3.2 P=P+{(u, v)};
3.3 V=V-{v};
3.4 u=v; //从顶点v出发继续求解
代码实现如下:
#include
#include
const int amount = 5;
int TSP1(int arc[amount][amount], int temp)
{
int Count_hyh= 0, TSPLen_hyh = 0;
int min_hyh, u, v;
int tem[amount] = {0};
u = temp; tem[temp] = 1;
while (Count_hyh< amount-1)
{
min_hyh = 100;
for (int j = 0; j < amount; j++)
if ((tem[j] == 0) && (arc[u][j] != 0) && (arc[u][j] < min_hyh)) {
v = j;
min_hyh = arc[u][j];
}
TSPLen_hyh += arc[u][v];
tem[v] = 1; Count_hyh++;
cout<"<0"<
(二)最短链接策略
每次在整个图的范围内选择最短边加入到解集合中,但是,要保证加入解集合中的边最终形成一个哈密顿回路。因此,当从剩余边集E'中选择一条边(u, v)加入解集合S中,应满足以下条件:① 边(u, v)是边集E'中代价最小的边;② 边(u, v)加入解集合S后,S中不产生回路;③ 边(u, v) 加入解集合S后,S中不产生分枝;
设图G有n个顶点,边上的代价存储在二维数组w[n][n]中,集合E'是候选集合即存储所有未选取的边,集合P存储经过的边,最短链接策略求解TSP问题的算法如下:
1.P={ };
2.E'=E; //候选集合,初始时为图中所有边
3.循环直到集合P中包含n-1条边
3.1 在E'中选取最短边(u, v);
3.2 E'=E'-{(u, v)};
3.3 如果 (顶点u和v在P中不连通 and 不产生分枝)
则P=P+{(u, v)};
实现上述伪代码的难点在于判断u和v在P中是否连通,笔者解决的办法是采用并查集。用一个数组实现:int father[N];,其中father[i]表示元素i的父亲节点,而父亲节点本身也是这个集合内的元素,father[1]=2就表示元素1的父亲结点是元素2,以这种父亲关系来表示元素所属的集合。若father[i]=i,则说明元素i是该集合的根节点。并查集的基本操作包括:初始化、查找、合并。对于初始化,一开始每个元素都是独立的一个集合,则令所有的i的father[i]=i;查找就是给定对给定的节点寻找其根结点的过程,即通过递推或者递归反复寻找父亲结点;合并就是把两个集合合为一个,先判断它们是否属于同一集合,再选择是否合并。当然,还可以压缩路径,即把当前查询结点的路径上的所有结点的父亲都指向根节点。
实现代码如下:
#include
#include
using namespace std;
#define N 50
typedef struct point
{
int value;
int from;
int end;
}point;
int point_set[N];
int radio[N];
point result[N];
int Find(int x)
{
int root,parent;
root = x;
while(point_set[root] != root)
root = point_set[root];
parent = point_set[x]; //路径压缩
while(root != parent)
{
point_set[x] = root;
x = parent;
parent = point_set[x];
}
return root;
}
int Union(int a,int b)
{
int root_a = Find(a);
int root_b = Find(b);
point_set[root_b] = root_a;
return root_a;
}
bool com_point(point a,point b)
{
return a.value < b.value;
}
int TSP_Short(int dis[][N],int n,point point[])
{
int temp = (n*(n-1))/2; // 边的个数
int i;
int count = 0;
int k = 0;
for(i = 0; i < temp; i++)
if(point[i].value != 0)
{
if((Find(point[i].from) != Find(point[i].end))&&radio[point[i].from] != 2 && radio[point[i].end] != 2)
{
count += point[i].value;
radio[point[i].from]++;
radio[point[i].end]++;
result[k].value = point[i].value;
result[k].from = point[i].from;
result[k].end = point[i].end;
Union(point[i].from,point[i].end);
k++;
if(k == n -1)
break;
}
}
return count;
}
int main()
{
int n;
printf("请输入顶点个数:");
scanf("%d",&n);
int i,j;
int dis[N][N];
printf("输入%d阶对称矩阵:\n",n);
for(i = 1; i <= n; i++)
for(j = 1; j <= n; j++)
scanf("%d",&dis[i][j]);
point point[N];
int cost = 0;
int t = 0;
for(i = 1; i <= n; i++)
for(j = 1; j <= i-1; j++)
{
point[t].value = dis[i][j];
if(i < j)
{
point[t].from = i;
point[t].end = j;
}
else
{
point[t].from = j;
point[t].end = i;
}
t++;
}
sort(point,point+t,com_point);
for(i = 1; i <= n; i++)
point_set[i] = i;
memset(radio,0,sizeof(radio));
cost = TSP_Short(dis,n,point);
int end[2];
int x = 0;
for(i = 1; i <= n; i++)
{
if(radio[i] == 1)
end[x++] = i;
}
printf("边的两个顶点:");
for(i = 0; i < n-1; i++)
printf("v%d--v%d\t%d\n",result[i].from,result[i].end,result[i].value);
printf("v%d--v%d\t%d\n",end[0],end[1],dis[end[0]][end[1]]);
printf("%d\n",cost+dis[end[0]][end[1]]);
return 0;
}
以上就是TSP的贪心法求解,笔者还将对TSP用回溯法求解,感兴趣的可以关注笔者博客。