问题描述:
比如此图的路线:
从【1】出发遍历每一个点而后回到【1】,走哪条路线更短(有一点点像哈密顿图的形式)
分析:
我们有【1234】、【1243】、【1324】、【1342】、【1423】、【1432】共六种走法,似乎可以直接遍历搜索,但如果节点数增加到很多,那就是一个问题了,所以我们用回溯遍历的方法,在回溯的过程中注意【剪枝】即可。
输入数据:
此处我们采用数组(矩阵)来输入数据:
int p[10][10], pre[10], shunxu[10];//p用来存放数据,pre用来标记以便于后序剪枝操作,shunxu用来标记走哪个点。
int ans = INF, sum = 0, N;
int main()
{
int i, j, min[10];
scanf("%d",&N);
for(i = 0; i <= N; i ++)
min[i] = INF;
for(i = 1; i <= N; i ++)
shunxu[i] = i;
memset(p,0,sizeof(p));
memset(pre,0,sizeof(pre));
for(i = 1; i <= N; i++)
{
for(j = i + 1; j <= N; j++)
{
int s;
scanf("%d",&s);
p[i][j] = p[j][i] = s;
if(min[i] > s)
min[i] = s;
if(min[j] > s)
min[j] = s;
}
}
for(i = 1; i <= N; i++)
pre[i] = pre[i - 1] + min[i];
traceback(1);
printf("%d\n",ans);
return 0;
}
回溯:
这里的回溯是排列树操作,有点类似与【n皇后问题】但又有所不同,不同就在于一是这里的矩阵不仅每行选列要不同,而且还得保证最后回到了原点,因此,我们仍然是用【n皇后】的那种思想,但每次操作的数据就有所不同了:
void traceback(int t)
{
if(t > N)
{
sum += p[shunxu[t - 1]][shunxu[1]];
if(sum < ans)
ans = sum;
sum -= p[shunxu[t - 1]][shunxu[1]];
return;
}
if(sum + pre[N] - pre[t - 1] > ans)
return;//剪枝操作,意思就是就算后面所有的取值都是最小值的话也不能使得sum的值小于ans,那就没有继续下去的必要了,那莫,立刻return掉。
for(i = t; i <= N; i++)
{
swap(&shunxu[i], &shunxu[t]);
sum += p[shunxu[t - 1]][shunxu[t]];
traceback(t + 1);
sum -= p[shunxu[t - 1]][shunxu[t]];
swap(&shunxu[i], &shunxu[t]);
}
return;
}
那这里求得的就是整个图的最短的回路值了,其实也就是从【1】到【1】的最短回路了,从时间的角度出发,从哪里开始我们还是只求从哪里开始的值,因此我们应该添加一些判定条件,完整代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define INF 0x3f3f3f3f
int p[10][10], shunxu[10], pre[10];
int ans = INF, sum;
int N;
void swap(int *a, int *b)
{
int temp;
temp = *a;
*a = *b;
*b = temp;
return;
}
void traceback(int t);
int main()
{
int i, j, min[10];
scanf("%d",&N);
for(i = 0; i <= N; i ++)
min[i] = INF;
for(i = 1; i <= N; i ++)
shunxu[i] = i;
memset(p,0,sizeof(p));
memset(pre,0,sizeof(pre));
for(i = 1; i <= N; i++)
{
for(j = i + 1; j <= N; j++)
{
int s;
scanf("%d",&s);
p[i][j] = p[j][i] = s;
if(min[i] > s)
min[i] = s;
if(min[j] > s)
min[j] = s;
}
}
for(i = 1; i <= N; i++)
pre[i] = pre[i - 1] + min[i];
traceback(1);
printf("%d\n",ans);
system("pause");
return 0;
}
void traceback(int t)
{
int i;
if(t > N)
{
sum += p[shunxu[t - 1]][shunxu[1]];
if(sum < ans)
ans = sum;
for (i = 1; i <= N; i++)
{
printf("%d ", shunxu[i]);
}
printf("sum = %d\n", sum);
sum -= p[shunxu[t - 1]][shunxu[1]];
return;
}
if(sum + pre[N] - pre[t - 1] > ans)
return;//剪枝操作,意思就是就算后面所有的取值都是最小值的话也不能使得sum的值小于ans,那就没有继续下去的必要了,那莫,立刻return掉。
if(t == 1)
{
traceback(t + 1);
return;
}//因为是从【1】出发,这里添加限定条件可以省掉一部分时间。
for(i = t; i <= N; i++)
{
swap(&shunxu[i], &shunxu[t]);
sum += p[shunxu[t - 1]][shunxu[t]];
traceback(t + 1);
sum -= p[shunxu[t - 1]][shunxu[t]];
swap(&shunxu[i], &shunxu[t]);
}
return;
}