tsp问题 订阅
旅行商问题,即TSP问题(Traveling Salesman Problem)又译为旅行推销员问题、货郎担问题,是数学领域中著名问题之一。假设有一个旅行商人要拜访n个城市,他必须选择所要走的路径,路径的限制是每个城市只能拜访一次,而且最后要回到原来出发的城市。路径的选择目标是要求得的路径路程为所有路径之中的最小值。 展开全文
旅行商问题,即TSP问题(Traveling Salesman Problem)又译为旅行推销员问题、货郎担问题,是数学领域中著名问题之一。假设有一个旅行商人要拜访n个城市,他必须选择所要走的路径,路径的限制是每个城市只能拜访一次,而且最后要回到原来出发的城市。路径的选择目标是要求得的路径路程为所有路径之中的最小值。
信息
领    域
数学
属    于
组合优化问题
别    名
旅行推销员问题、货郎担问题
中文名
旅行商问题
性    质
具有NPC计算复杂性
外文名
Travelling Salesman Problem
TSP问题简介
TSP问题是一个组合优化问题。该问题可以被证明具有NPC计算复杂性。因此,任何能使该问题的求解得以简化的方法,都将受到高度的评价和关注。旅行推销员问题是图论中最著名的问题之一,即“已给一个n个点的完全图,每条边都有一个长度,求总长度最短的经过每个顶点正好一次的封闭回路”。Edmonds,Cook和Karp等人发现,这批难题有一个值得注意的性质,对其中一个问题存在有效算法时,每个问题都会有有效算法。 [1]  迄今为止,这类问题中没有一个找到有效算法。倾向于接受NP完全问题(NP-Complete或NPC)和NP难题(NP-Hard或NPH)不存在有效算法这一猜想,认为这类问题的大型实例不能用精确算法求解,必须寻求这类问题的有效的近似算法。此类问题中,经典的还有 子集和问题; Hamilton回路问题;最大团问题。
收起全文
精华内容
下载资源
问答
  • TSP问题

    2020-10-16 14:19:19
  • tsp问题

    千次阅读 2015-10-11 20:10:02
    旅行商问题(tsp问题)就是一个经销商从n个城市中的某一城市出发,不重复的走完其余的n-1个城市并回原出发点,求所有可能路径单中走出路径长度最短的一条。  本题假设旅行商从第一个城市出发、

       旅行商问题(tsp问题)就是一个经销商从n个城市中的某一城市出发,不重复的走完其余的n-1个城市并回原出发点,求所有可能路径单中走出路径长度最短的一条。

     本题假设旅行商从第一个城市出发。

      解法:dfs+回溯

    #include <iostream>
    
    using namespace std;
    
    #define NUM 100
    
    int n;//图G的顶点数
    int m;//图G的边数
    int a[NUM][NUM];//图G的邻接矩阵
    int x[NUM] = {0}; //当前解
    int bestx[NUM];//最优解
    int cc = 0; //当前费用
    int bestc = 0; //当前最优值
    int NoEdge = -1; //无边标记
    int Visited[100];//访问标志
    int num = 0;
    
    int FirstArc(int t)
    {
        for(int i = 1; i <= m; i++)
            if(a[t][i] != NoEdge)
                return i;
        return 0;
    }
    
    int NextArc(int t, int w)
    {
        for(int p = w + 1; p <= m; p++)
            if(a[t][p] != NoEdge)
                return p;
        return 0;
    }
    
    void dfs(int t)
    {
        int l, i;
        Visited[t] = 1;
    
        if(num == m && a[t][1] != NoEdge)
        {
            if(cc + a[t][1] < bestc)
            {
                bestc = cc + a[t][1];
                for(int s = 0; s < m; s++)
                    bestx[s] = x[s];
            }
        }
        else
        {
            for(i = FirstArc(t); i != 0; i = NextArc(t, i))
                if(!Visited[i] && a[t][i] != NoEdge)
                    if(cc + a[t][i] < bestc) //剪枝
                    {
                        x[num++] = i;
                        cc += a[t][i];
                        dfs(i);
                        cc -= a[t][i];
                        Visited[i] = 0;
                        num--;
                    }
        }
    }
    
    void tsp(int t)
    {
        for(int i = 1; i <= m; i++)
            Visited[i] = 0;
    
        //保存第一个节点
        x[num++] = t;
        Visited[t] = 1;
    
        for(int q = FirstArc(t); q != 0; q = NextArc(t, q))
            if(a[t][q] != NoEdge && !Visited[q])
            {
                x[num++] = q;
                cc += a[t][q];
                dfs(q);
                cc -= a[t][q]; //回溯
                Visited[q] = 0;
                num--;
            }
    }
    
    
    int main()
    {
        int s, e, t;
        scanf("%d%d", &m, &n);
        //初始化邻接矩阵
        for(int i = 1; i <= m; i++)
            for(int j = 1; j <= m; j++)
                a[i][j] = NoEdge;    //表示两个节点不相连
    
        for(int i = 1; i <= n; i++)
        {
            scanf("%d%d%d", &s, &e, &t);
            a[s][e] = t;
            a[e][s] = t;
        }
        //初始化变量
        bestc = 1000;
        tsp(1);
    
        for(int i = 0; i < m; i++)
            printf("%d ", bestx[i]);
    
        printf("\nbestc=%d\n", bestc);
        return 0;
    }
    
    /*
    1 2 20
    1 4 4
    1 3 6
    2 3 5
    2 4 10
    3 4 15
    
    */
    




    展开全文
  • tsp问题程序

    2016-08-23 19:32:19
    tsp问题程序
  • matlab解决TSP问题蚁群算法-蚁群算法解决TSP问题.doc 在求解TSP问题中有比较好的应用,大家可以看一下!!
  • 遗传算法解决TSP问题MATLAB实现(详细)

    万次阅读 多人点赞 2019-02-01 15:49:05
    TSP问题也称为货郎担问题,是一个古老的问题。最早可以追溯到1759年Euler提出的骑士旅行的问题。1948年,由美国兰德公司推动,TSP成为近代组合优化领域的典型难题。 TSP是一个具有广泛的应用背景和重要理论价值的...

    问题定义:巡回旅行商问题
    给定一组n个城市和俩俩之间的直达距离,寻找一条闭合的旅程,使得每个城市刚好经过一次且总的旅行距离最短。
    TSP问题也称为货郎担问题,是一个古老的问题。最早可以追溯到1759年Euler提出的骑士旅行的问题。1948年,由美国兰德公司推动,TSP成为近代组合优化领域的典型难题。
    TSP是一个具有广泛的应用背景和重要理论价值的组合优化问题。 近年来,有很多解决该问题的较为有效的算法不断被推出,例如Hopfield神经网络方法,模拟退火方法以及遗传算法方法等。
    TSP搜索空间随着城市数n的增加而增大,所有的旅程路线组合数为(n-1)!/2。在如此庞大的搜索空间中寻求最优解,对于常规方法和现有的计算工具而言,存在着诸多计算困难。借助遗传算法的搜索能力解决TSP问题,是很自然的想法。
    基本遗传算法可定义为一个8元组:
    (SGA)=(C,E,P0,M,Φ,Г,Ψ,Τ)
    C ——个体的编码方法,SGA使用固定长度二进制符号串编码方法;
    E ——个体的适应度评价函数;
    P0——初始群体;
    M ——群体大小,一般取20—100;
    Ф——选择算子,SGA使用比例算子;
    Г——交叉算子,SGA使用单点交叉算子;
    Ψ——变异算子,SGA使用基本位变异算子;
    Т——算法终止条件,一般终止进化代数为100—500;
    问题的表示
    对于一个实际的待优化问题,首先需要将其表示为适合于遗传算法操作的形式。用遗传算法解决TSP,一个旅程很自然的表示为n个城市的排列,但基于二进制编码的交叉和变异操作不能适用。
    路径表示是表示旅程对应的基因编码的最自然,最简单的表示方法。它在编码,解码,存储过程中相对容易理解和实现。例如:旅程(5-1-7-8-9-4-6-2-3)可以直接表示为(5 1 7 8 9 4 6 2 3)
    产生初始种群
    一是完全随机产生,它适合于对问题的解无任何先验知识的情况。随机性较强,因而也较公正
    二是某些先验知识可转变为必须满足的一组要求,然后在满足这些要求的解中在随机地选取样本。这样选择初始种群可使遗传算法更快的达到最优解。种群有一定的目标性和代表性,但取例不如完全随机的广泛,而且先验知识是否可靠也是一个问题
    适应度函数
    遗传算法在进化搜索中基本不利用外部信息,仅以适应度函数为依据,利用种群中每个个体的适应度值来进行搜索。TSP的目标是路径总长度为最短,路径总长度的倒数就可以为TSP的适应度函数:
    在这里插入图片描述

    选择
    一般地说,选择将使适应度较大(优良)个体有较大的存在机会,而适应度较小(低劣)的个体继续存在的机会也较小。简单遗传算法采用赌轮选择机制,令Σfi表示群体的适应度值之总和,fi表示种群中第i个染色体的适应度值,它产生后代的能力正好为其适应度值所占份额fi/Σfi。
    交叉
    基于路径表示的编码方法,要求一个个体的染色体编码中不允许有重复的基因码,也就是说要满足任意一个城市必须而且只能访问一次的约束。基本遗传算法的交叉操作生成的个体一般不能满足这一约束条件。
    部分匹配交叉操作要求随机选取两个交叉点,以便确定一个匹配段,根据两个父个体中两个交叉点之间的中间段给出的映射关系生成两个子个体。
    在这里插入图片描述
    变异
    遗传算法解决TSP 问题基于二进值编码的变异操作不能适用,不能够由简单的变量的翻转来实现
    在TSP问题中个体的编码是一批城市的序列,随机的在这个序列抽取两个城市,然后交换他们的位置。这样就实现了个体编码的变异,算法如下:
    产生两个0到1之间的随机实数;
    将这两个随机实数转化为0到n(城市数)-1之间的随机整数;
    将这两个随机整数指代的城市进行交换;
    流程图
    在这里插入图片描述
    代码
    完整代码参考我的网站:http://www.omegaxyz.com/2019/01/21/matlab-tsp-all/

    主函数代码:

    clear;
    clc;
    tStart = tic; % 算法计时器
    %%%%%%%%%%%%自定义参数%%%%%%%%%%%%%
    [cityNum,cities] = Read('dsj1000.tsp');
    cities = cities';
    %cityNum = 100;
    maxGEN = 1000;
    popSize = 100; % 遗传算法种群大小
    crossoverProbabilty = 0.9; %交叉概率
    mutationProbabilty = 0.1; %变异概率
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    gbest = Inf;
    % 随机生成城市位置
    %cities = rand(2,cityNum) * 100;%100是最远距离
    % 计算上述生成的城市距离
    distances = calculateDistance(cities);
    % 生成种群,每个个体代表一个路径
    pop = zeros(popSize, cityNum);
    for i=1:popSize
    pop(i,:) = randperm(cityNum); 
    end
    offspring = zeros(popSize,cityNum);
    %保存每代的最小路劲便于画图
    minPathes = zeros(maxGEN,1);
    % GA算法
    for  gen=1:maxGEN
    % 计算适应度的值,即路径总距离
    [fval, sumDistance, minPath, maxPath] = fitness(distances, pop);
    % 轮盘赌选择
    tournamentSize=4; %设置大小
    for k=1:popSize
    % 选择父代进行交叉
    tourPopDistances=zeros( tournamentSize,1);
    for i=1:tournamentSize
    randomRow = randi(popSize);
    tourPopDistances(i,1) = sumDistance(randomRow,1);
    end
    % 选择最好的,即距离最小的
    parent1  = min(tourPopDistances);
    [parent1X,parent1Y] = find(sumDistance==parent1,1, 'first');
    parent1Path = pop(parent1X(1,1),:);
    for i=1:tournamentSize
    randomRow = randi(popSize);
    tourPopDistances(i,1) = sumDistance(randomRow,1);
    end
    parent2  = min(tourPopDistances);
    [parent2X,parent2Y] = find(sumDistance==parent2,1, 'first');
    parent2Path = pop(parent2X(1,1),:);
    subPath = crossover(parent1Path, parent2Path, crossoverProbabilty);%交叉
    subPath = mutate(subPath, mutationProbabilty);%变异
    offspring(k,:) = subPath(1,:);
    minPathes(gen,1) = minPath; 
    end
    fprintf('代数:%d   最短路径:%.2fKM \n', gen,minPath);
    % 更新
    pop = offspring;
    % 画出当前状态下的最短路径
    if minPath < gbest
    gbest = minPath;
    paint(cities, pop, gbest, sumDistance,gen);
    end
    end
    figure 
    plot(minPathes, 'MarkerFaceColor', 'red','LineWidth',1);
    title('收敛曲线图(每一代的最短路径)');
    set(gca,'ytick',500:100:5000); 
    ylabel('路径长度');
    xlabel('迭代次数');
    grid on
    tEnd = toc(tStart);
    fprintf('时间:%d 分  %f 秒.\n', floor(tEnd/60), rem(tEnd,60));
    

    完整代码参考我的网站:http://www.omegaxyz.com/2019/01/21/matlab-tsp-all/

    结果

    测试数据:
    在这里插入图片描述
    初始状态:
    在这里插入图片描述
    最终状态:
    在这里插入图片描述

    收敛曲线图:
    在这里插入图片描述
    总结与观点

    难点是交叉算法的设计,由于TSP问题和一般的NP问题不一样,每个个体的每个维度具有唯一性,因此在交叉的时候要注意不能有重复的值。本次实验采用的是部分匹配交叉,先从第一个父代选出一个偏移量,从偏移量后的部分点加入到子代,接下来从第二个父代选择第一代没有选择的部分点移到子代中。
    当城市数量较多时,大于50个城市,迭代多次,GA仍然不收敛,可能的问题是陷入了局部最优解,因此对GA算法进行改进怡跳出局部最优解,可以采用类似于PSO或者蚁群算法的思想。

    更多内容访问 omegaxyz.com
    网站所有代码采用Apache 2.0授权
    网站文章采用知识共享许可协议BY-NC-SA4.0授权
    © 2020 • OmegaXYZ-版权所有 转载请注明出处

    展开全文
  • TSP问题算法

    2018-06-17 23:15:32
    TSP问题算法,以及在通信网中路由选择问题的类似算法。
  • 蚁群算法解TSP问题

    2020-10-03 10:21:31
    蚁群算法解TSP问题MATLAB;;蚁群算法解TSP问题MATLAB;蚁群算法解TSP问题MATLAB;蚁群算法解TSP问题MATLAB;蚁群算法解TSP问题MATLAB;蚁群算法解TSP问题MATLAB
  • 遗传算法求解TSP问题

    2021-04-18 22:06:22
    遗传算法解决TSP问题
  • TSP问题总结归纳

    万次阅读 多人点赞 2018-10-26 13:02:07
    TSP问题即旅行商问题,经典的TSP可以描述为:一个商品推销员要去若干个城市推销商品,该推销员从一个城市出发,需要经过所有城市后,回到出发地。应如何选择行进路线,以使总的行程最短。从图论的角度来看,该问题...

    TSP问题即旅行商问题,经典的TSP可以描述为:一个商品推销员要去若干个城市推销商品,该推销员从一个城市出发,需要经过所有城市后,回到出发地。应如何选择行进路线,以使总的行程最短。从图论的角度来看,该问题实质是在一个带权完全无向图中,找一个权值最小的哈密尔顿回路。

    旅行商问题有很多种不同的问法,最近做了几个关于TSP的题,下面总结一下。由于大部分TSP问题都是NP-Hard的,因此很难得到什么高效的多项式级别的算法,一般采用的算法都偏向于暴力搜索以及状压DP,这里都采取用状压DP解决。大部分TSP问题所给的地点数目都非常小。

    考虑经典的TSP问题,如果采用状压DP,将每个地点访问与否作为二进制1/0压缩,不难得到状态转移方程:
    dp[S][i] = min(dp[S][i], dp[S ^ (1 << (i - 1))][k] + dist[k][i])
    S代表当前状态,i(从1开始)表示到达当前状态时最后访问的是第i个地点
    k为S中所有访问的与i不同的地点。
    dist表示两点间最短路。
    以及初始化:
    DP[S][i] = dist[start][i](S == 1<<(i - 1))
    如果初次遇到状压dp,感到陌生的话,就请仔细思考上面式子的含义,这是大部分TSP问题的关键。

    经典TSP——HDU-5067–Harry And Dig Machine

    题目链接
    题意:给定一个n*m的地图,地图上的数为0表示不需要挖掘,大于0则表示需要挖掘,
    现在有一台挖掘机在地图的最左上方的位置,需要将所有需要挖掘的地方挖掘一次,
    然后回到地图原点,问挖掘机所走的最短路程是多少?

    分析:因为这个题是一个地图,每个位置之间都是连通的,因此不难想到挖掘机走的最短路程肯定是每个挖掘点只走一次的情况,那么本题就是一个典型的TSP问题,地图上没有任何障碍,因此可以直接用两点的 横坐标差的绝对值+纵坐标差的绝对值代表两点之间的距离,然后将每个点的状态压缩,利用状压dp求解即可。
    AC代码:

    #include <stdio.h>
    #include <math.h>
    #include <memory.h>
    #include <string.h>
    const int maxN = 55;
    int dp[1 << 11 ][11];
    int dist[11][11];
    int pos[11][2];
    
    int min(int a , int b)
    {
        if (a > b) return b;
        return a;
    }
    void Init(int posNum)
    {
        for (int i = 0 ; i <= posNum ; i++)
        {
            for (int j = 1 ; j <= posNum ; j ++ )
            {
                if ( i == 0 )
                {
                    dist[j][i] = dist[i][j] = pos[j][0] + pos[j][1] - 2;
                }
                else {
                    dist[j][i] = dist[i][j] = abs(pos[i][0] - pos[j][0]) + abs(pos[i][1] - pos[j][1]);
                }
            }
        }
    }
    
    int main()
    {
        int n, m;
        while (scanf("%d%d",&n,&m)!=EOF)
        {
            int posNum = 0;
            for (int i = 1 ; i <= n ; i ++ )
            {
                for (int j = 1 ; j <= m ; j ++ )
                {
                    int num;
                    scanf("%d",&num);
                    if ( num > 0 )
                    {
                        if (i == 1 && j == 1) continue;
                        posNum++;
                        pos[posNum][0] = i;
                        pos[posNum][1] = j;
                    }
                }
            }
            memset(dist, 0, sizeof(dist));
            Init(posNum);
            int stateNum = 1 << posNum;
            memset(dp, 0, sizeof(dp));
            for (int S = 0 ; S < stateNum ; S ++ )
            {
                for (int i = 1 ; i <= posNum ; i ++ )
                {
                    int tmp = 1 << (i - 1); 
                    if (S & tmp)
                    {
                        if (S == tmp)
                        {
                            dp[S][i] = dist[0][i];
                        }
                        else {
                            dp[S][i] = 0x3f3f3f3f;
                            for (int k = 1 ; k <= posNum ; k ++ )
                            {
                                if ( (S & (1 << (k - 1) )) && k != i  )
                                {
                                    dp[S][i] = min(dp[S][i], dp[S ^ (1 << (i - 1))][k] + dist[k][i]);
                                }
                            }
                        }
                    }
                }
            }
            int ans = 1e8;
            for (int i = 1 ; i <= posNum ; i ++ )
            {
                ans = min(ans, dp[stateNum - 1][i] + dist[0][i]);
            }
            if (ans == 1e8) ans = 0;
            printf("%d\n", ans);
        }
        return 0;
    }
    

    需要用bfs处理距离-- POJ 2688–Cleaning Robot

    题目链接
    题意:
    本题和上题类似,也是给定一个地图,给定一个机器人的位置以及一些需要访问的点,求机器人访问所有点,但是不需要回到原点。地图上有一些障碍,障碍位置不能经过。

    分析:
    由于有障碍的存在无法直接得到每个访问点之间的距离,因此需要用bfs来处理最短路。
    后面状压DP即可。

    AC代码:

    #include <iostream>
    #include <queue>
    #include <memory>
    #include <string>
    #include <memory.h>
    #include <string.h>
    using namespace std;
    
    const int maxW = 25;
    const int maxN = 15;
    int map[maxW][maxW];
    int dirtymap[maxN][2];
    int dist[maxN][maxN];
    int deltaY[4] = { -1 , 1  , 0 , 0 };
    int deltaX[4] = { 0 , 0 , -1 , 1 };
    int dsDp[1<<12][maxN];
    bool visit[maxW][maxW];
    
    int min(int a, int b)
    {
    	if (a > b) return b;
    	return a;
    }
    
    struct Node
    {
    	int x;
    	int y;
    	int step;
    };
    
    int getDirtyIndex(int x , int y , int dirtyNum )
    {
    	for (int i = 1 ; i <= dirtyNum ; i ++ )
    	{
    		if (dirtymap[i][0] == y && dirtymap[i][1] == x)
    		{
    			return i;
    		}
    	}
    }
    
    bool bfs(int start , int dirtyNum,int width , int height )
    {
    	memset(visit, false, sizeof(visit));
    	int dir = 0;
    	int sY = dirtymap[start][0];
    	int sX = dirtymap[start][1];
    	visit[sY][sX] = true;
    	queue<Node> nodeQue;
    	Node startN;
    	startN.x = sX; startN.y = sY; startN.step = 0;
    	nodeQue.push(startN);
    	while (dir < dirtyNum && !nodeQue.empty())
    	{
    		Node node = nodeQue.front();
    		nodeQue.pop();
    		if (!(node.y == sY&& node.x == sX))
    		{
    			if (map[node.y][node.x] == '*')
    			{
    				int dirtyIndex = getDirtyIndex(node.x, node.y, dirtyNum);
    				dist[start][dirtyIndex] = dist[dirtyIndex][start] = node.step;
    				dir++;
    			}
    			else if (map[node.y][node.x] == 'o')
    			{
    				dist[0][start] = dist[start][0] = node.step;
    				dir++;
    			}
    		}
    		for (int i = 0 ;  i< 4 ; i ++ )
    		{
    			int nX = node.x + deltaX[i];
    			int nY = node.y + deltaY[i];
    			if (nX >= 1 && nX <= width && nY >= 1 && nY <= height && map[nY][nX] != 'x' && visit[nY][nX] == false)
    			{
    				Node nNode;
    				nNode.x = nX;
    				nNode.y = nY;
    				nNode.step = node.step + 1;
    				visit[nY][nX] = true;
    				nodeQue.push(nNode);
    			}
    		}
    	}
    	return dir == dirtyNum;
    }
    
    int dp(int dirtyNum )
    {
    	memset(dsDp, 0, sizeof(dsDp));
    	int stateNum = 1 << dirtyNum;
    	for (int i = 0 ; i < stateNum ; i ++ )
    	{
    		for (int j = 1 ;  j <= dirtyNum ; j ++ )
    		{
    			int tmp = 1 << (j - 1);
    			if ( i & tmp )
    			{
    				if ( i == tmp )
    				{
    					dsDp[i][j] = dist[0][j];
    				}
    				else {
    					dsDp[i][j] = 1e8;
    					for (int k = 1 ; k <= dirtyNum ; k ++ )
    					{
    						if ( i & ( 1 << (k - 1)) && k != j  )
    						{
    							dsDp[i][j] = min(dsDp[i ^ (1 << (j - 1))][k] + dist[k][j] , dsDp[i][j]);
    						}
    					}
    				}
    			}
    		}
    	}
    	int ans = 1e8;
    	for (int i = 1 ;  i <= dirtyNum ; i ++ )
    	{
    		ans = min(ans, dsDp[stateNum - 1][i]);
    	}
    	return ans;
    }
    
    int main()
    {
    	int width, height;
    	while ( cin >> width >> height && !( width == 0 && height == 0 ))
    	{
    		int dirtyNum = 0;
    		for (int i = 1 ; i <= height ; i ++ )
    		{
    			for (int j = 1 ; j <= width ; j ++ )
    			{
    				char d;
    				cin >> d;
    				map[i][j] = d;
    				if (d == '*')
    				{
    					dirtyNum++;
    					dirtymap[dirtyNum][0] = i;
    					dirtymap[dirtyNum][1] = j;
    				}
    				else if (d == 'o')
    				{
    					dirtymap[0][0] = i;
    					dirtymap[0][1] = j;
    				}
    			}
    		}
    		bool suc = true;
    		for (int i = 0 ; i <= dirtyNum ; i ++ )
    		{
    			if (bfs(i,dirtyNum,width,height) == false)
    			{
    				suc = false;
    				break;
    			}
    		}
    		if (!suc) cout << -1 << endl;
    		else cout << dp(dirtyNum) << endl;
    	}
    }
    

    无上界访问次数的TSP问题——POJ 3311 Hie with the Pie

    题目链接

    题意:
    给定一个图的邻接矩阵,现要求从1号点出发,所有需要访问的点都至少被访问一次之后回到原点,问所走的最短路程是多少。

    分析:
    注意此题是所有需要访问的点至少访问一次,也就是访问次数无上界,因此在处理每个点之间的最短距离的时候要用Floyd算法,用Floyd算法就代表了访问次数是有可能为多次,其余的依旧正常状压DP。

    AC代码:

    #include <iostream>
    using namespace std;
    const int maxN = 20;
    int map[maxN][maxN];
    int dp[maxN][65540];
    
    int min(int a, int  b)
    {
    	if (a > b) return b;
    	return a;
    }
    void floyd(int n)
    {
    	for (int i = 1 ; i <= n ; i ++ )
    	{
    		for (int j = 1 ; j <= n ; j ++ )
    		{
    			for (int k = 1 ; k <= n ; k ++ )
    			{
    				if (map[j][i] + map[i][k] < map[j][k] && i != j && k != j && i != k )
    				{
    					map[j][k] = map[j][i] + map[i][k];
    				}
    			}
    		}
    	}
    }
    
    int main()
    {
    	int n;
    	while (cin >> n && n != 0 )
    	{
    		for (int i = 1 ;  i <= n + 1 ; i ++ )
    		{
    			for (int j = 1;  j <= n + 1 ; j ++ )
    			{
    				int d;
    				cin >> d;
    				map[i][j] = d;
    			}
    		}
    		floyd(n + 1);
    		int maxS = ( 1 << ( n ) ) ;
    		for (int j = 0; j < maxS; j++)
    		{
    		for (int i = 2 ;  i <= n + 1 ; i ++ )
    		{
    				if ( j & ( 1 << (i - 2) ))
    				{
    					if (j == (1 << (i - 2)))
    					{
    						dp[i][j] = map[1][i];
    					}
    					else {
    						dp[i][j] = 1 << 30;
    						for (int k = 2 ; k <= n + 1 ; k ++ )
    						{
    							if ((j & (1 << (k - 2))) && k != i)
    							{
    									dp[i][j] = min(dp[i][j],dp[k][j ^ (1 << (i - 2))] + map[k][i]);
    							}
    						}
    					}
    				}
    			}
    		}
    		int ans = (1 << 31) - 1;
    		for (int j = 2 ; j <= n + 1 ; j ++ )
    		{
    				ans = min(ans,dp[j][(1 << n) - 1] + map[j][1]);
    		}
    		cout << ans << endl;
    	}
    	return 0;
    }
    

    至多访问2次的TSP问题–HDU 3001–Traveling

    题目链接

    题意:有n个城市,起始在任意一个城市,给出每两个城市之间的一条路的长度,现要求访问所有城市至多两次至少一次所需要的走最短路程。

    分析:本题也是不知道最短路,但是不能用Floyd处理,因为这里严格限制了每个城市至多访问2次,用Floyd有可能处理过程中访问了多次,而且这个最短路似乎也不是特别好求,那么就可以穷举所有的情况,有些城市有可能会访问2次,那么把所有的城市访问0、1、2次的情况全部考虑一遍不就行了吗?那么这个题就直接状压DP,问题的关键是如何表示这个访问次数,之前的状压DP都是用2进制表示的,现在需要多一个访问2次的情况,这就是这个题最精妙的地方——采用三进制表示状态,0表示不访问,1表示访问1次,2表示访问2次,具体的看代码吧。

    AC代码:

    #include <iostream>
    #include <memory.h>
    #include <string.h>
    
    using namespace std;
    
    int three[15];
    int map[15][15];
    int dp[70000][15];
    #define INF 0x3f3f3f3f
    
    int min(int a , int b)
    {
    	if (a < b) return a;
    	return b;
    }
    int main()
    {
    	three[0] = 1;
    	for (int i = 1; i <= 10; i++) three[i] = three[i - 1] * 3;
    	int n, m;
    	while (scanf("%d%d",&n,&m) != EOF)
    	{
    		int from, to, val;
    		for (int i = 0; i <= n; i++)
    			for (int j = 0; j <= n; j++)
    				map[i][j] = 1e8;
    		while (m--)
    		{
    			scanf("%d%d%d", &from, &to, &val);
    			from--; to--;
    			map[from][to] = map[to][from] = min(map[from][to], val);
    		}
    		memset(dp, INF, sizeof(dp));
    		int ans = 1e8;
    		for (int i = 0; i <= 10; i++) dp[three[i]][i] = 0;
    		for (int i = 0 ; i < three[n] ; i ++ )
    		{
    			bool getAns = true;
    			for (int j = 0 ; j < n ; j ++)
    			{
    				if (  ( (i / three[j]) % 3 ) == 0 )
    					getAns = false;
    			}
    			for (int j = 0 ; j < n ; j ++ )
    			{
    				for (int k = 0 ; k < n ; k ++ )
    				{
    					if ( ( i / three[k] ) % 3 < 2 && j != k )
    					{
    						int u = i + three[k];
    						dp[u][k] = min(dp[u][k], dp[i][j] + map[k][j]);
    					}
    				}
    			}
    			if (getAns)
    			{
    				for (int j = 0; j < n; j++) ans = min(ans, dp[i][j]);
    			}
    		}
    	
    		if (ans == 1e8) ans = -1;
    		printf("%d\n", ans);
    	}
    
    	return 0;
    }
    

    在三进制下没法做到像二进制那样做位运算,因此稍微麻烦一点。如果题目问至多访问2次、3次、4次……,都可以采用类似的方法解决。

    上面这样几种TSP问题都是在访问次数上做一些限制,碰到此类问题,如果能够利用某种方法求出符合题目要求的访问点之间的最短路径,就可以直接先求最短路径,然后按经典TSP问题解决;而如果没有办法求出访问点之间的最短路径,就需要穷举所有的访问方式。

    多TSP问题–HDU 4281–Judges’ response

    题目链接
    题意:
    给定n个提问者的坐标以及解答该提问者问题的时间,现有裁判可以解决问题,给出每个裁判为解决问题所能花费的最大时间(所有裁判相同)。
    求:
    1.至少需要多少个裁判?
    2.假设裁判可以有无限个,每个裁判一开始全部位于提问者一的位置,裁判需要回答某几个问题然后回到原处,如何安排裁判解答问题,可以使得裁判走得总路程最少?

    分析:
    第一问是一个背包问题,将每个提问者的问题是否得到解答作为0/1状态压缩,然后预处理出每个裁判所能达到的所有状态(即在该状态下解答所有需要解答问题的时间不超过裁判所能花费的最大时间),将每种状态看成是背包的物品,状态就是所占的容量,状态转移方程为:
    dp[i] = min(dp[i],dp[i ^ state[j]] + 1)(j为合法状态并且i&state[j]==state[j])

    第二问就是多TSP问题,在本题中有可能一个裁判无法解决所有的问题,那么如果要解决所有的问题,就可以用多个裁判来解决,由于所有裁判的最大花费时间是相同的,因此可以先求解一次TSP问题,得到每个裁判到达其能到达的状态时所走的最短路程(这个路程就是欧氏距离,不需要特别的处理),然后合并这些状态,best[i]表示状态i下的最短路程,best[i]=min(best[i],best[j]+best[i^j]) j为i的所有子状态,且状态j和状态i^j都是裁判能够到达的状态。

    AC代码:

    #include <stdio.h>
    #include <math.h>
    const int maxN = 16;
    int cost[maxN + 1];
    int dp[70000];
    int tspDp[70000][maxN+1];
    int best[70000];
    int pos[maxN+1][2];
    int totalState;
    int S[7000];
    int dist[maxN+1][maxN+1];
    #define INF 1e8
    
    int min(int a , int b )
    {
        if (a > b) return b;
        return a;
    }
    
    void InitDist(int n)
    {
        for (int i = 1; i <= n; i++)
        {
            for (int j = 1 ; j <= n ; j ++ )
            {
                int d = (pos[i][0] - pos[j][0]) * (pos[i][0] - pos[j][0]) + (pos[i][1] - pos[j][1]) * (pos[i][1] - pos[j][1]);
                dist[i][j] = ceil(sqrt((double)d));
            }
        }
    }
    
    bool check(int state , int n , int maxCost )
    {
        int sum = 0;
        for (int i = 0 ; i < n ; i ++  )
        {
            if ( state & ( 1 << i ) )
            {
                sum += cost[i + 1];
            }
        }
        return sum <= maxCost;
    }
    
    int dpFunc(int n , int total)
    {
        for (int i = 0; i < (1 << n); i++)dp[i] = INF;
        dp[0] = 0;
        for (int i = 1 ; i <= total ; i ++ )
        {
            for (int j = ( (1 << n)  - 1 ) ; j >= 0 ; j -- )
            {
                if ( ( (j & S[i]) == S[i]) && dp[j ^ S[i]] != INF )
                {
                    dp[j] = min(dp[j], dp[j^S[i]] + 1);
                }
            }
        }
        return dp[(1 << n) - 1] == INF ? -1 : dp[(1 << n) - 1];
    }
    
    int mtsp(int total , int n )
    {
        for (int i = 0; i < (1 << n); i++)     best[i] = INF;
        for (int i = 1 ; i <= total ; i ++ )
        {
            for (int j = 0 ; j < n ; j ++ )
            {
                if ( S[i] &  ( 1 << j ) )
                {
                    if ( S[i] == ( 1 << j ) )
                    {
                        tspDp[S[i]][j] = dist[1][j + 1];
                    }
                    else {
                        tspDp[S[i]][j] = INF;
                        for (int k = 0 ; k < n ; k ++ )
                        {
                            if ( (S[i] & (1 << k))  && k != j )
                            {
                                tspDp[S[i]][j] = min(tspDp[S[i]][j], tspDp[S[i] ^ (1 << j)][k] + dist[k+1][j+1]);
                            }
                        }
                    }
                    best[S[i]] = min(best[S[i]], tspDp[S[i]][j] + dist[j+1][1]);
                }
            }
        }
        for(int i = 0 ; i < ( 1 << n) ; i ++ )
        {
            if ( i & 1 )
            {
                for (int j = i & ( i - 1 ) ; j ; j = i & ( j - 1 ) )
                {
                    best[i] = min(best[i], best[j] + best[i^j]);
                }
            }
        }
        return best[(1 << n) - 1] == INF ? -1 : best[(1 << n) - 1];
    }
    
    int main()
    {
        int n, m;
    
        while ( scanf( "%d%d" ,&n, &m ) != EOF )
        {
            int x, y;
            totalState = 0;
            for (int i = 1 ; i <= n ; i ++ )
            {
                scanf("%d%d", &x, &y);
                pos[i][0] = x;
                pos[i][1] = y;
            }
            for (int i = 1 ; i <= n ; i ++ )
            {
                int c;
                scanf("%d", &c);
                cost[i] = c;
            }
            for (int i = 0 ; i <  (1 << n)  ; i ++ )
            {
                if (check(i,n,m))
                {
                    totalState++;
                    S[totalState] = i;
                }
            }
            InitDist(n);
            int ans = dpFunc(n, totalState);
            int ans2 = mtsp(totalState, n);
            printf("%d %d\n", ans, ans2);
    
        }
    
        return 0;
    }
    

    POJ 2677–Tour–双调TSP问题

    双调TSP问题是比较特殊的TSP问题,它对TSP问题走的路线做了限制:只能从最左端开始出发,然后严格地从左向右走到最右端,再从最右端严格地从右走向左回到出发点,这种问题可以在多项式复杂度内解决。

    在这里插入图片描述
    双调TSP其实就是走如图所示的这样一种闭合回路。
    设dp[i][j]表示从i出发向左走到1 然后再从1出发向右走到j的最短距离
    状态转移,当i<j-1时,走到j之前的最后一个点一定是j-1,因此dp[i][j] = dp[i][j-1] + dist[j-1][j]
    当i=j-1时,走到j之前的最后一个点是不确定的,因此dp[j-1][j] = min(dp[j-1][j] , dp[k][j-1] + dist[k][j])

    最终答案为dp[n][n] = dp[n-1][n] + dist[n-1][n]

    AC代码:

    #include <stdio.h>
    #include <algorithm>
    #include <math.h>
    const int maxN = 205;
    
    double min(double a , double b )
    {
    	if (a > b) return b;
    	return a;
    }
    double dp[maxN][maxN];
    struct Node
    {
    	double x;
    	double y;
    	Node() {};
    	Node(double xx ,double yy)
    	{
    		x = xx;
    		y = yy;
    	};
    	bool operator < (const Node & n)
    	{
    		return this->x < n.x;
    	};
    };
    Node nodeArr[maxN];
    double dist(int i , int j )
    {
    	return sqrt((nodeArr[i].x - nodeArr[j].x) * (nodeArr[i].x - nodeArr[j].x) + (nodeArr[i].y - nodeArr[j].y) * (nodeArr[i].y - nodeArr[j].y));
    }
    
    int main()
    {
    	int n;
    	while (scanf("%d", &n) != EOF)
    	{
    		for (int i = 1 ; i <= n ; i ++ )
    		{
    			double x, y;
    			scanf("%lf%lf", &x, &y);
    			nodeArr[i] = Node(x, y);
    		}
    		std::sort(nodeArr + 1, nodeArr + n + 1 );
    		for (int i = 1; i <= n; i++)
    			for (int j = 1; j <= n; j++)
    				dp[i][j] = 0;
    		dp[1][2] = dist(1, 2);
    		for (int i =3 ;  i<= n ; i ++ )
    		{
    			for (int j = 1 ; j < i - 1 ; j ++	)
    			{
    				dp[j][i] = dp[j][i-1] + dist(i-1,i);
    			}
    			dp[i - 1][i] = 1e8;
    			for (int j = 1 ; j < i - 1 ; j ++ )
    			{
    				dp[i - 1][i] = min(dp[j][i-1] + dist(i, j),dp[i-1][i]);
    			}
    		}
    		dp[n][n] = dp[n - 1][n] + dist(n - 1, n);
    		printf("%.2f\n", dp[n][n] );
    	}
    
    	return 0;
    }
    
    展开全文
  • 1、理论概述1.1、TSP问题旅行商问题,即TSP问题(旅行推销员问题、货郎担问题),是数学领域中著名问题之一。假设有一个旅行商人要拜访n个城市,他必须选择所要走的路径,路径的限制是每个城市只能拜访一次,而且最后...
  • 遗传算法 求解旅行商 TSP 问题,matlab代码

    万次阅读 多人点赞 2016-11-02 01:24:11
    学习启发式算法时,旅行商问题是一个经典的例子。其中,遗传算法可以用来求解该问题。遗传算法是一种进化算法,由于其启发式算法的属性,并不能保证得到最优解。求解效果与初始种群选取,编码方法,选择方法,交叉...
  • 遗传算法解决TSP问题

    2020-01-24 05:10:18
    资源包含“遗传算法解决TSP问题”的相关代码(.cpp和.h)以及TSP相关的城市数据。 资源包含“遗传算法解决TSP问题”的相关代码(.cpp和.h)以及TSP相关的城市数据。

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 5,535
精华内容 2,214
关键字:

tsp问题