全排列与回溯法解旅行商问题

虽然说全排列好像很简单,但真的当用程序来生成时还一时不知道怎么办,但是我觉得穷举法是很多算法的基础,比如回溯法,排序等,我会以旅行商问题来说明全排列,只需一次就会了。


1.开始

回溯法的本质也是搜索,只是加上了约束条件,使搜索的次数减少许多,我们把约束条件和目标函数的边界称为剪枝策略。

对于旅行商这个问题,说的是一个商人想从一个城市出发,不重复地走遍所有城市然后回到起点,每个城市间有不同的旅行费用,求花最少的钱走完?

典型的一个图的存储结构,如下例子:

1

我采用简单的二维矩阵存储结构,接下来:
我会给出穷举的算法,然后加上约束条件,最后进行对比。

穷举法

穷举次序列的本质是全排列,采用递归生成,打印全过程:
仔细的看代码就好,注释很详细,全排列的精髓就在那了:

#include <iostream>
using namespace std;

int *x;//城市编号数组
int *bestx;//路线
int w = 0;//过渡变量
int bestw = INT32_MAX;//最优的费用

void Tsp(int **a, int n, int s);
int main() {
    int **a;//a[i][j]表示从第i个城市到第j个的费用
    int n;//城市个数

    cin >> n;

    x = new int[n];
    for (int i = 0; i < n; i++) {
        x[i] = i;//表示第i个城市编号,0到n-1
    }
    bestx = new int[n];

    a = new int*[n];
    for (int i = 0; i < n; i++) {
        a[i] = new int[n];
    }
    //输入费用矩阵表示图
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            cin >> a[i][j];
        }
    }

    Tsp(a, n, 1);//传1表示只搜从0开始的全排,传0表示所有全排

    cout << "最优的是:" << bestw << endl;

    for (int i = 0; i < n; i++) {
        delete a[i];
    }
    delete[]a;
    delete[]x;
    delete[]bestx;
    return 0;
}
void Tsp(int **a, int n, int s) {
    if (s == n) {
        w = 0;//清零
        cout << "bestx: ";
        for (int i = 0; i < n; i++) {
            bestx[i] = x[i];
            cout << bestx[i] << " ";
            if (i < n - 1) {
                w += a[x[i]][x[i + 1]];
            }
            else {
                w += a[x[i]][x[0]];//回到起点的费用
            }
        }
        cout << "w:" << w << endl;
        if (bestw > w) {
            bestw = w;//更新最优值
        }
    }
    else {
        for (int i = s; i < n; i++) {
            //为什么要交换呢?因为要将x[s]作为起点进行往下搜索
            int t = x[i]; x[i] = x[s]; x[s] = t;
            Tsp(a, n, s + 1);
            t = x[i]; x[i] = x[s]; x[s] = t;
        }
    }
}

运行结果如下:

5
0 3 3 2 6
3 0 7 3 2
3 7 0 2 5
2 3 2 0 3
6 2 5 3 0
bestx: 0 1 2 3 4 w:21
bestx: 0 1 2 4 3 w:20
bestx: 0 1 3 2 4 w:19
bestx: 0 1 3 4 2 w:17
bestx: 0 1 4 3 2 w:13
bestx: 0 1 4 2 3 w:14
bestx: 0 2 1 3 4 w:22
bestx: 0 2 1 4 3 w:17
bestx: 0 2 3 1 4 w:16
bestx: 0 2 3 4 1 w:13
bestx: 0 2 4 3 1 w:17
bestx: 0 2 4 1 3 w:15
bestx: 0 3 2 1 4 w:19
bestx: 0 3 2 4 1 w:14
bestx: 0 3 1 2 4 w:23
bestx: 0 3 1 4 2 w:15
bestx: 0 3 4 1 2 w:17
bestx: 0 3 4 2 1 w:20
bestx: 0 4 2 3 1 w:19
bestx: 0 4 2 1 3 w:23
bestx: 0 4 3 2 1 w:21
bestx: 0 4 3 1 2 w:22
bestx: 0 4 1 3 2 w:16
bestx: 0 4 1 2 3 w:19
最优的是:13
请按任意键继续. . .

回溯法

现在,我们加上约束条件:

#include "stdafx.h"
#include <iostream>
using namespace std;

int *x;//城市编号数组
int *bestx;//最好的路线
int bestw = 0;//最优的费用
int cc = 0;//过渡变量cost

void Tsp(int **a, int n, int s);
int main() {
    int **a;//a[i][j]表示从第i个城市到第j个的费用
    int n;//城市个数

    cin >> n;

    x = new int[n];
    for (int i = 0; i < n; i++) {
        x[i] = i;//表示第i个城市编号,0到n-1
    }
    bestx = new int[n];

    a = new int*[n];
    for (int i = 0; i < n; i++) {
        a[i] = new int[n];
    }
    //输入费用矩阵表示图
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            cin >> a[i][j];
        }
    }

    Tsp(a, n, 1);

    cout << "最优的是:" << bestw << endl;

    for (int i = 0; i < n; i++) {
        delete a[i];
    }
    delete[]a;
    delete[]x;
    delete[]bestx;
    return 0;
}
void Tsp(int **a, int n, int s) {
    if (s == n) {
        //找到一条路线,下面判断条件的含义
        //a[x[n - 1]][x[0]] != 0:0表示城市不可达
        //cc + a[x[n - 1]][x[0]] < bestw:加上返回起点的费用是否大于最优的费用
        if ((a[x[n - 1]][x[0]] != 0 && (cc + a[x[n - 1]][x[0]] < bestw)) || bestw == 0) {
            cout << "bestx: ";
            for (int i = 0; i < n; i++) {
                bestx[i] = x[i];
                cout << bestx[i] << " ";
            }
            bestw = cc + a[x[n - 1]][x[0]];
            cout << "bestw:" << bestw << endl;
        }
    }
    else {
        for (int i = s; i < n; i++) {
            //这里就是递归遍历的地方,加了约束条件,和上面一样
            //递归的基础是:前面交换了,回溯时得交换回来,加了得减回来
            if ((a[x[i - 1]][x[i]] != 0 && cc + a[x[i - 1]][i] < bestw) || bestw == 0) {
                int t = x[i]; x[i] = x[s]; x[s] = t;
                cc += a[x[s - 1]][x[s]];
                Tsp(a, n, s + 1);
                cc -= a[x[s - 1]][x[s]];
                t = x[i]; x[i] = x[s]; x[s] = t;
            }
        }
    }
}

再来看运行结果:

2

总结

可以看出约束条件还是很有用的。
全排列的大体框架都差不多,操作都在s==n里。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值