TSP旅行商——路径问题综述
TSP旅行商——NP问题
NP问题:时间复杂度是以n为底的多项式,如O(N2), O(N3), 统称Np问题, 也可以说只能由暴力搜索解决的问题。
非NP问题:时间复杂度是非多项式,如O(log2n)
题目描述:
给 n
个城市(从 1
到 n
),城市和无向道路成本
之间的关系为3元组 [A, B, C]
(在城市 A
和城市 B
之间有一条路,成本是 C
)我们需要从1开始找到的旅行所有城市的付出最小的成本。
- 一个城市只能通过一次。
- 你可以假设你可以到达
所有
的城市。
示例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];
}
}
}
}