旅行商问题(TSP问题)

题目
TSP问题(旅行商问题)是指旅行家要旅行n个城市,要求各个城市经历且仅经历一次然后回到出发城市,并要求所走的路程最短。
假设现在有四个城市,0,1,2,3,他们之间的代价如图一,可以存成二维表的形式
这里写图片描述 这里写代码片
现在要从城市0出发,最后又回到0,期间1,2,3都必须并且只能经过一次,使代价最小。

分析
TSP问题是NP完全问题;
从初始点出发的周游路线一共有(n-1)!条,即等于除初始结点外的n-1个结点的排列数,因此旅行商问题是一个排列问题。通过枚举(n-1)!条周游路线,从中找出一条具有最小成本的周游路线的算法,其计算时间显然为O(n!)。

方法1:动态规划方法
动态规划可行性:设s, s1, s2, …, sp, s是从s出发的一条路径长度最短的简单回路,假设从s到下一个城市s1已经求出,则问题转化为求从s1到s的最短路径,显然s1, s2, …, sp, s一定构成一条从s1到s的最短路径,所以TSP问题是构成最优子结构性质的,用动态规划来求解也是合理的。
推倒动态规划方程:
假设从顶点s出发,令d(i, V’)表示从顶点i出发经过V’(是一个点的集合)中各个顶点一次且仅一次,最后回到出发点s的最短路径长度。
推导:(分情况来讨论)
①当V’为空集,那么d(i, V’),表示从i不经过任何点就回到s了,如上图的 城市3->城市0(0为起点城市)。此时d(i, V’)=Cis(就是 城市i 到 城市s 的距离)、
②如果V’不为空,那么就是对子问题的最优求解。你必须在V’这个城市集合中,尝试每一个,并求出最优解。
d(i, V’)=min{Cik + d(k, V’-{k})} 注:Cik表示你选择的城市和城市i的距离,d(k, V’-{k})是一个子问题。
综上所述,TSP问题的动态规划方程就出来了:
这里写图片描述

将d(i, V’)转换成二维表,d[i][j]

实现


public class Num7 {

    private static int[][] d;
    private static int[] bits;
    /**
     * @param args
     */
    public static void main(String[] args) {
         int[][] c={ 
                    {0,3,6,7}, 
                    {5,0,2,3}, 
                    {6,4,0,2}, 
                    {3,7,5,0} 
                }; 
         getMinCost(c);

    }

    /*动态规划 实现TSP问题
     * 
     * 用二进制表示集合(比如1100,表示在当前集合中:节点1、节点2不存在,节点3和节点4存在)
     * 
     * 从上到下求,递归
     */
    public static void getMinCost(int[][] a){
        int n=a.length;
        bits=new int[n];
        for(int i=0;i<n;i++){
            bits[i]=1<<i;
        }

        d=new int[1000][n];

        for(int i=0;i<d.length;i++){
            for(int j=0;j<d[0].length;j++){
                d[i][j]=-1;
            }
        }
        int t=1;
        for(int i=1;i<n;i++){  //初始化这些值:{1}->1   {2}->2    {3}-3
            d[t<<(i-1)][i]=a[0][i];
        }

        int res=Integer.MAX_VALUE;

        t=0;
         for(int i=1;i<n;i++){
                t=t<<1;
                t=t|1;
        }
        for(int i=1;i<n;i++){
            int r=getD(i,t,n,a);
            if(r+a[i][0]<res){
                res=r+a[i][0];
            }
        }


        System.out.println(res);
    }

    private static int getD(int i, int t,int n,int[][] a) {
        int min=Integer.MAX_VALUE;

        if(d[t][i]!=-1){
            return d[t][i];
        }

        t=t&(~bits[i-1]);

        for(int j=1;j<n;j++){
            int tt=t&bits[j-1];   //遍历每个集合中的节点
            if(tt>0){   
                int res=getD(j,t,n, a);
                if(res+a[j][i]<min){
                    min=res+a[j][i];
                }
            }
        }

        d[t][i]=min;
        return d[t][i];
    }

}
  • 11
    点赞
  • 82
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值