题目地址:
https://www.lintcode.com/problem/traveling-salesman-problem/description
旅行商问题,给定一个有权无向图,顶点 1 ∼ n 1\sim n 1∼n,问从顶点 1 1 1出发,走遍所有顶点的花费最小是多少。题目不保证没有平行边。
思路是DFS + 剪枝。首先题目不保证没有平行边,建图的时候要注意,两个顶点的距离,如果有多条边的话,就按小的来算就好。此外,如果发现当前的cost已经大于等于了之前算出来过的某个cost,就直接剪枝返回。代码如下:
import java.util.Arrays;
public class Solution {
private int res = Integer.MAX_VALUE;
/**
* @param n: an integer,denote the number of cities
* @param roads: a list of three-tuples,denote the road between cities
* @return: return the minimum cost to travel all cities
*/
public int minCost(int n, int[][] roads) {
// Write your code here
// 用邻接矩阵建图
int[][] graph = new int[n][n];
for (int[] row : graph) {
Arrays.fill(row, Integer.MAX_VALUE);
}
// 注意是无向图,所以要建双向边,并且如果之前已经建过边了,新边来的时候要取小的
for (int[] road : roads) {
int len = graph[road[0] - 1][road[1] - 1], minLen = Math.min(len, road[2]);
graph[road[0] - 1][road[1] - 1] = graph[road[1] - 1][road[0] - 1] = minLen;
}
dfs(0, 0, 1, graph, new boolean[n]);
return res;
}
// cur是当前的出发点,cost是已经产生的耗费,n是已经访问过的顶点数
private void dfs(int cur, int cost, int n, int[][] graph, boolean[] visited) {
// 如果已经访问了所有顶点,则更新答案并返回
if (n == graph.length) {
res = Math.min(res, cost);
return;
}
// 如果当前的花费已经大于了之前求过的某个答案,直接返回,不可能是最优解
if (cost >= res) {
return;
}
// 标记当前顶点为已经访问
visited[cur] = true;
// 遍历其未访问的邻居,进行访问。新的cost和n要传进去
for (int i = 0; i < graph[cur].length; i++) {
if (graph[cur][i] != Integer.MAX_VALUE && !visited[i]) {
dfs(i, cost + graph[cur][i], n + 1, graph, visited);
}
}
// 回溯的时候恢复现场
visited[cur] = false;
}
}
时间复杂度 O ( V + E ) O(V+E) O(V+E),空间 O ( V ) O(V) O(V)。