【旅行售货员问题】回溯搜索

问题描述:
在这里插入图片描述
比如此图的路线:在这里插入图片描述
从【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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值