TSP旅行商问题——回溯法

任务描述
在这里插入图片描述

输入格式
第一行输入n,代表有n个城市。
接下来n行每行输入n个数,第i行j列的值代表i市到j市的距离,0代表城市之间不通
注意:起点和终点都为1
n<7,城市之间的距离都不超过100

输出格式
第一行输出最少的旅行费用
第二行输入旅行路径
(保证只有一条最短旅行路径)

Sample Input

4
0 30 6 4
30 0 5 10
6 5 0 20
4 10 20 0

Sample Output

25
1 3 2 4 1

#include<stdio.h>
#define MAX 100
int n;                          //城市个数
int c[MAX][MAX];                //城市间距离
int point[MAX];                 //记录点
int bestPath[MAX] = { 0 };      //记录最优路径
int minLen = 63355;             //最短路径长
int nowLen = 0;                 //当前路径长

void swap(int* a, int* b) {
    int t = *a;
    *a = *b;
    *b = t;
}

void backpack(int t) {//t = 2,假设从1点开始
    if (t > n) {//当t = 5时,说明要找回路了
        //如果最后一个点能直接回到起点,且路线最短,存储到bestPath
        if ((c[point[n]][1]) && (c[point[n]][1] + nowLen < minLen)) {
            minLen = c[point[n]][1] + nowLen;
            for(int i = 1; i <= n; i++)  bestPath[i] = point[i];
        }
    }
    else {
        for (int i = t; i <= n; i++) {
            //如果路径存在,并且加入这条路径后总长更短
            //如果加入当前路径不会更短,那就会i++, 就会导致i和t不一样,就要交换点,调用swap
            if ((c[point[t - 1]][point[i]]) && (nowLen + c[point[t - 1]][point[i]] < minLen)) {
                if (i != t) swap(&point[t], &point[i]);
                nowLen += c[point[t - 1]][point[t]];//加入这条路线长度
                backpack(t + 1);//查看下一个点
                //上面是t+1,当走完所有景点,t+1>n,backpack(t+1)if语句走完就要执行下面的语句,也就是回溯   
                nowLen -= c[point[t - 1]][point[t]];
                if (i != t) swap(&point[t], &point[i]);
            }
        }
    }
}

int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) point[i] = i;//将各个顶点放入集合
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            scanf("%d", &c[i][j]);
        }
    }
    backpack(2);//默认先以1为出发点
    printf("%d\n", minLen);//输出最短距离
    for (int i = 1; i <= n; i++) printf("%d ", bestPath[i]);//输出旅行路线
    printf("%d", bestPath[1]);
    return 0;
}

可能swap的功能会比较难以理解
下面的代码只是相比上面的代码多了一些向人们展示了代码的运行过程的代码,可以通过观察运行结果加深对代码执行过程的认识

#include<stdio.h>
#define MAX 100
int n;    //城市个数
int cnt = 1;
int c[MAX][MAX];                   //城市间距离
int point[MAX];                       //记录点
int bestPath[MAX] = { 0 };           //记录最优路径
int minLen = 63355;              //最短路径长
int nowLen = 0;                    //当前路径长



void swap(int* a, int* b) {
    int t = *a;
    *a = *b;
    *b = t;
}

void backpack(int t) {//t = 2,假设从1点开始
    if (t > n) {//当t = 5时,说明要找回路了
        //如果最后一个点能直接回到起点,且路线最短,存储到bestPath
        if ((c[point[n]][1]) && (c[point[n]][1] + nowLen < minLen)) {
            minLen = c[point[n]][1] + nowLen;
            printf("我找到了目前最优路径:");
            for(int i = 1; i <= n; i++) {
                bestPath[i] = point[i];
                printf("%d ", point[i]);
            }
            printf("\n");
        }
    }
    else {
        for (int i = t; i <= n; i++) {
            //如果路径存在,并且加入这条路径后总长更短
            //如果加入当前路径不会更短,那就会i++, 就会导致i 和 t不一样,就要交换点
            //后面走完所有路径且有回路,bestPath会把这些点序存储起来
            if ((c[point[t - 1]][point[i]]) && (nowLen + c[point[t - 1]][point[i]] < minLen)) {
                if (i != t) {
                    printf("交换前point[%d] = %d point[%d] = %d\n", i, point[i], t, point[t]);
                    swap(&point[t], &point[i]);
                    printf("交换后point[%d] = %d point[%d] = %d\n", i, point[i], t, point[t]);
                    printf("第%d次点交换:", cnt++);
                    for (int j = 1; j <= i; j++) {
                        printf("%d ", point[j]);
                    }
                }
                
                //printf("\n");
                nowLen += c[point[t - 1]][point[t]];//加入这条路线长度
                printf("加入了一个点%d\n", point[i]);
                backpack(t + 1);//查看下一个点
                //上面是t+1,当走玩所有景点,t+1>n,backpack(t+1)if语句走完就要执行下面的语句
                printf("回溯了\n");
                nowLen -= c[point[t - 1]][point[t]];
                if (i != t) {
                    printf("交换前point[%d] = %d point[%d] = %d\n", i, point[i], t, point[t]);
                    swap(&point[t], &point[i]);
                    printf("交换后point[%d] = %d point[%d] = %d\n", i, point[i], t, point[t]);
                    printf("第%d次点交换:", cnt++);
                    for (int j = 1; j <= i; j++) {
                        printf("%d ", point[j]);
                    }
                }
                printf("\n");
            }
        }
    }
}
/*
4
0 30 6 4
30 0 5 10
6 5 0 20
4 10 20 0
*/
int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        point[i] = i;//将各个顶点放入集合
      
    }   
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            scanf("%d", &c[i][j]);
        }
    }
    backpack(2);//默认先以1为出发点
    printf("%d\n", minLen);//输出最短距离
    for (int i = 1; i <= n; i++) printf("%d ", bestPath[i]);//输出旅行路线
    printf("%d", bestPath[1]);
    return 0;
}

  • 4
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值