TSP旅行商——路径问题综述

TSP旅行商——NP问题

NP问题:时间复杂度是以n为底的多项式,如O(N2), O(N3), 统称Np问题, 也可以说只能由暴力搜索解决的问题。

非NP问题:时间复杂度是非多项式,如O(log2n)

题目描述:

n 个城市(从 1n),城市和无向道路成本之间的关系为3元组 [A, B, C](在城市 A 和城市 B 之间有一条路,成本是 C)我们需要从1开始找到的旅行所有城市的付出最小的成本。

  1. 一个城市只能通过一次。
  2. 你可以假设你可以到达所有的城市。

示例1:

输入: 
	n = 3
	tuple = [[1,2,1],[2,3,2],[1,3,3]]
输出: 3
说明:最短路是1->2->3

示例2:

输入:
	n = 1
	tuple = []
输出: 0

题解:暴力搜索:DFS + 剪枝

时间复杂度:O(n! * n)

class Solution {
    int[][] map;
    int[] visited;
    int result =Integer.MAX_VALUE;

    public void dfs(int val, int cost, int n, int count) {
        if(count == n) {
            result = Math.min(result, cost);
            return;
        }
        if(cost > result) return;

        //System.out.println(val+":"+cost+" "+count);
        int min = Integer.MAX_VALUE;
        int next = 0;

        for(int i=1; i<=n; i++) {
            if(map[val][i]!=Integer.MAX_VALUE && visited[i] == 0) {
                visited[i] = 1;
                dfs(i, cost+ map[val][i], n, count+1);
                visited[i] = 0;
            }
        }

    }

    public int minCost(int n, int[][] roads) {
        if(roads.length == 0 || roads[0].length == 0) return 0;

        map = new int[n + 1][n + 1];
        for (int i = 0; i <= n; i++) {
            Arrays.fill(map[i], Integer.MAX_VALUE);
        }
        visited = new int[n + 1];

        int a, b, c;
        for (int i = 0; i < roads.length; i++) {
            a = roads[i][0];
            b = roads[i][1];
            c = roads[i][2];
            map[a][b] = Math.min(c, map[a][b]);
            map[b][a] = Math.min(c, map[b][a]);
        }

        visited[1] = 1;
        dfs(1, 0, n, 1);

        return result;
    }
}

题解:状态压缩动态规划

时间复杂度:O(2n * n2)

dp表示:

dp[state][city]:state代表已经经过的城市,city是当前达到的城市
state:用二进制表示已经经过的城市,如经过1,3,5,则二进制是10101, 也就是1+4+16=21

状态转移方程:

dp[state][i] = min{dp[state - 2^(i-1)][j] + graph[j][i]}
public class Solution {
        public int minCost(int n, int[][] roads) {
            int minCost = 1000000000;
            int[][] graph = new int[n + 1][n + 1];

            //构图
            for (int i = 0; i <= n; i++) {
                Arrays.fill(graph[i], minCost);
            }
            for (int i = 0; i < roads.length; i++) {
                graph[roads[i][0]][roads[i][1]] = Math.min(graph[roads[i][0]][roads[i][1]], roads[i][2]);
                graph[roads[i][1]][roads[i][0]] = Math.min(graph[roads[i][1]][roads[i][0]], roads[i][2]);
            }

            int state = 1 << n;                                 //5个城市,state = 100000
            int[][] dp = new int[state][n + 1];
            for (int i = 0; i < dp.length; i++) {
                Arrays.fill(dp[i], minCost);
            }
            dp[1][1] = 0;

            //状压动态规划
            for (int s = 1; s < state; s++) {
                for (int i = 2; i <= n; i++) {
                    if ((s & (1 << (i - 1))) == 0) {            //i没有在state中,例如:100 & 10101 = 1, 3(100)在state(10101)中
                        continue;
                    }
                    int pre = s ^ (1 << (i - 1));               //dp[pre][i], pre是先前走过的城市
                    for (int j = 1; j <= n; j++) {
                        if ((pre & (1 << (j - 1))) == 0) {      //j没有在pre中
                            continue;
                        }
                        dp[s][i] = Math.min(dp[s][i], dp[pre][j] + graph[j][i]);
                    }
                }
            }

            for (int i = 1; i <= n; i++) {
                minCost = Math.min(minCost, dp[state - 1][i]);      //(state-1)的二进制=011111,即经过所有城市的最小值
            }
            return minCost;
        }
    }

其他解法:退火算法、遗传算法

Floyd算法:求任意两个城市之间的最短路径

for(int k = 1; k<=n; k++) 
    for(int i = 1; i<=n; i++) 
        for(int j = 1; j<=n; j++) 
            if(graph[i][j] > graph[i][k] + graph[k][j]) 
                graph[i][j] = graph[i][k] + graph[k][j]

Dijkstra算法:求一个点到其他顶点的最短路径,也叫做“单源最短路径”。如1到其他节点的最短路径

dis[j]: 1号顶点到其他各个顶点的距离

for(int i=1; i<=n-1; i++) {
    int min = Integer.MAX_VALUE, next = 0;
    
    //找打离1号顶点距离最近的顶点
    for(int j=1; j<=n; j++) {
        if(book[j] == 0 && dis[j] < min) {
            min = dis[j];
            next = j;
        }
    }
    
    book[next] = 1;
    for(int k=1; k<=n; k++) {
        if(graph[next][k] < Integer.MAX_VALUE) {
            if(dis[k] > dis[next] + graph[next][k]) {
                dis[k] = dis[next] + graph[next][k];
            }
        }
    }
}
posted @ 2022-05-17 15:02  言思宁  阅读(431)  评论(0编辑  收藏  举报