TSP旅行商问题
TSP是啥呢?通俗地讲,这个问题可以描述为一个商品推销员要去若干个城市推销商品,该推销员从一个城市出发,需要经过所有城市后,回到出发地。应如何选择行进路线,以使总的行程最短。所以就是在一个图中从一个点出发,找一个回路,能够回到这个点,而且总花销还最小。那我们怎么做呢?这篇文章我们用动态规划来写。
首先,请看一下bilibili上关于动态规划过程的描述,这是我们实现算法的基础:


我认为这两张图就比较通俗易懂了。我们从城市0出发,经过城市1,2,3最后回到城市0所花费的代价,是三个小代价的最小值,这三个小代价是什么呢?是城市0到城市1的距离,加上从城市1出发,经过城市2,3,最后回到城市0的距离,...后面以此类推,递推式已经写在上面了。因此,这个递推式就是我们实现递归的核心,我们需要不断更新d这个城市的集合(说白了,就是不断把它里面的城市从它里面剔除,一次剔除一个),这样,递归到最后,城市集合为空。这时候怎么办呢?别急,上面的式子里面也写了。递归到最后,我就只需要算集合外面那个城市(定义的是d(city,collection),这里集合外面的城市就指的是d的city)到城市0的距离即可,也就是说,直接把这个距离返回。这样,我们写代码的思路就出来了。然后再在前面做一些初始化的工作就可以了。
下面贴代码:
第一步,我们定义几个变量:
1 public int[][] dis; 2 public ArrayList<Integer> cities; 3 public int source;
第一行的二维数组dis存储的是距离,比如dis[i][j]表示的是从城市i到城市j的距离;
第二行的是集合,存储的是城市。由于城市我们以数字编号,因此使用了一个Integer类型的ArrayList集合;
第三行是源点,即城市0.
然后我们在构造器里来个初始化:
1 public Traveling(int n) { 2 cities = new ArrayList<>(); 3 for (int i = 0; i < n; ++i) { 4 cities.add(i); 5 } 6 source = 0; 7 }
在cities集合里添加全部的城市,这里城市编号从0开始。
然后我们写一个init方法,用来初始化dis数组,以什么样的标准呢?城市自己到自己的距离当然就是0啦,除此之外,全部设为正无穷。
1 public void init() { 2 int n = dis.length; 3 for (int i = 0; i < n; ++i) { 4 for (int j = 0; j < n; ++j) { 5 dis[i][j] = ((i == j) ? 0 : Integer.MAX_VALUE); 6 } 7 } 8 }
然后,为了实现后面递归代码里面城市集合几种不同的情况,我们写一个复制集合的方法(否则会炸,因为java不允许边迭代集合的时候还边对它里面的元素进行修改):
1 private ArrayList<Integer> copy(ArrayList<Integer> a) { 2 ArrayList<Integer> list = new ArrayList<>(); 3 for (Integer i : a) { 4 list.add(i); 5 } 6 return list; 7 }
最后,我们写一个distance函数,其实也就是tsp动态规划的过程:
1 private int distance(int start, ArrayList<Integer> list) { 2 if (list.size() == 0) { 3 return dis[start][source]; 4 } 5 int temp = Integer.MAX_VALUE; 6 for (Integer i : list) { 7 ArrayList<Integer> copied = copy(list); 8 copied.remove(i); 9 int res = distance(i, copied) + dis[start][i]; 10 temp = temp < res ? temp : res; 11 } 12 return temp; 13 }
首先,我们要定义递归函数的退出条件,即:城市集合为空时,我们应直接返回距离。
其次,遍历城市集合里面所有的城市,分别把它们挑出来,计算哪种花费的代价最小。我们就选那个,最后,把这个最小代价返回。
最后我们给外界提供一个方法:
1 public int tsp() { 2 return distance(source, cities); 3 }
来一波完整代码:
1 package com.hw.others; 2 3 import java.util.ArrayList; 4 import java.util.Scanner; 5 6 public class Traveling { 7 public int[][] dis; 8 public ArrayList<Integer> cities; 9 public int source; 10 11 public Traveling(int n) { 12 cities = new ArrayList<>(); 13 for (int i = 0; i < n; ++i) { 14 cities.add(i); 15 } 16 source = 0; 17 } 18 19 public void init() { 20 int n = dis.length; 21 for (int i = 0; i < n; ++i) { 22 for (int j = 0; j < n; ++j) { 23 dis[i][j] = ((i == j) ? 0 : Integer.MAX_VALUE); 24 } 25 } 26 } 27 28 private ArrayList<Integer> copy(ArrayList<Integer> a) { 29 ArrayList<Integer> list = new ArrayList<>(); 30 for (Integer i : a) { 31 list.add(i); 32 } 33 return list; 34 } 35 36 private int distance(int start, ArrayList<Integer> list) { 37 if (list.size() == 0) { 38 return dis[start][source]; 39 } 40 int temp = Integer.MAX_VALUE; 41 for (Integer i : list) { 42 ArrayList<Integer> copied = copy(list); 43 copied.remove(i); 44 int res = distance(i, copied) + dis[start][i]; 45 temp = temp < res ? temp : res; 46 } 47 return temp; 48 } 49 50 public int tsp() { 51 return distance(source, cities); 52 } 53 54 public static void main(String[] args) { 55 Scanner s = new Scanner(System.in); 56 System.out.println("输入城市个数:"); 57 int n = s.nextInt(); 58 Traveling traveler = new Traveling(n); 59 traveler.dis = new int[n+1][n+1]; 60 traveler.init(); 61 System.out.println("输入边的数目:"); 62 int edge = s.nextInt(); 63 System.out.println("现在输入城市与城市间的距离(城市编号从0开始):"); 64 for (int i = 0; i < edge; ++i) { 65 int start = s.nextInt(); 66 int end = s.nextInt(); 67 int weight = s.nextInt(); 68 traveler.dis[start][end] = weight; 69 traveler.dis[end][start] = weight; 70 } 71 int length = traveler.tsp(); 72 System.out.println("最短路径代价为:" + length); 73 s.close(); 74 } 75 }

浙公网安备 33010602011771号