旅行商问题
给 n 个城市(从 1 到 n),城市和无向道路成本之间的关系为3元组 [A, B, C](在城市 A 和城市 B 之间有一条路,成本是 C)我们需要从1开始找到的旅行所有城市的付出最小的成本。
import java.util.Arrays;
import java.util.List;
public class Solution {
/**
* O(n!)
* 从 start开始,遍历完所需最小代价
*
* @param roads
* @param start
* @return
*/
private static int solve1(int[][] roads, List<Integer> posts, int start) {
int num = 0;
/**
* 集合中没有邮局,则结束
*/
for (int i = 0; i < posts.size(); ++i) {
if (posts.get(i) != null) {
num++;
}
}
if (num == 1) {
return 0;
}
/**
* 设置当前邮局为已访问
*/
posts.set(start - 1, null);
int min = Integer.MAX_VALUE;
for (int i = 0; i < posts.size(); ++i) {
if (posts.get(i) != null && roads[start - 1][i] != Integer.MAX_VALUE) {
int next = solve1(roads, posts, i + 1);
if (next != Integer.MAX_VALUE) {
min = Math.min(min, next + roads[start - 1][i]);
}
}
}
posts.set(start - 1, 1);
return min;
}
private static int lowBit(int n) {
return n & (-n);
}
private static int reset(int cityStatus, int index) {
return cityStatus & (~(1 << (index - 1)));
}
private static int set(int cityStatus, int index) {
return cityStatus | (1 << (index - 1));
}
private static boolean exist(int cityStatus, int index) {
return (cityStatus & (1 << (index - 1))) != 0;
}
/**
* O(n!)
*
* @param roads
* @param cityStatus
* @param start
* @return
*/
private static int solve2(int[][] roads, int cityStatus, int start) {
/**
* 只剩一个邮局
*/
if (lowBit(cityStatus) == cityStatus) {
return 0;
}
/**
* 设置当前邮局为已访问
*/
cityStatus = reset(cityStatus, start);
int min = Integer.MAX_VALUE;
for (int i = 0; i < roads.length; ++i) {
if ((cityStatus & (1 << i)) != 0 && roads[start - 1][i] != Integer.MAX_VALUE) {
int next = solve2(roads, cityStatus, i + 1);
if (next != Integer.MAX_VALUE) {
min = Math.min(min, next + roads[start - 1][i]);
}
}
}
return min;
}
private static int solve3(int[][] roads, int cityStatus, int start, int[][] dp) {
if (dp[cityStatus][start - 1] != -1) {
return dp[cityStatus][start - 1];
}
/**
* 只剩一个邮局
*/
if (lowBit(cityStatus) == cityStatus) {
dp[cityStatus][start - 1] = 0;
return 0;
}
/**
* 设置当前邮局为已访问
*/
cityStatus = reset(cityStatus, start);
int min = Integer.MAX_VALUE;
for (int i = 0; i < roads.length; ++i) {
if ((cityStatus & (1 << i)) != 0 && roads[start - 1][i] != Integer.MAX_VALUE) {
int next = solve3(roads, cityStatus, i + 1, dp);
if (next != Integer.MAX_VALUE) {
min = Math.min(min, next + roads[start - 1][i]);
}
}
}
cityStatus = set(cityStatus, start);
dp[cityStatus][start - 1] = min;
return min;
}
/**
* O(2^n * n ^ 2)
*/
private static int solve4(int[][] roads) {
int n = roads.length;
int[][] dp = new int[1 << n][n];
for (int status = 1; status <= (1 << n) - 1; ++status) {
/**
* 一个邮局,默认就是0
*/
if (lowBit(status) == status) {
continue;
}
for (int start = 0; start < n; ++start) {
if (exist(status, start + 1)) {
int min = Integer.MAX_VALUE;
int newStatus = reset(status, start + 1);
for (int k = 0; k < n; k++) {
if ((newStatus & (1 << k)) != 0 && roads[start][k] != Integer.MAX_VALUE) {
int next = dp[newStatus][k];
if (next != Integer.MAX_VALUE) {
min = Math.min(min, next + roads[start][k]);
}
}
}
dp[status][start] = min;
}
}
}
return dp[(1 << n) - 1][0];
}
public static int minCost(int n, int[][] roads) {
if (n <= 1 || roads.length == 0 || roads[0].length == 0) {
return 0;
}
int[][] newRoads = new int[n][n];
for (int i = 0; i < n; ++i) {
Arrays.fill(newRoads[i], Integer.MAX_VALUE);
}
for (int i = 0; i < roads.length; ++i) {
newRoads[roads[i][0] - 1][roads[i][1] - 1] = Math.min(newRoads[roads[i][0] - 1][roads[i][1] - 1], roads[i][2]);
newRoads[roads[i][1] - 1][roads[i][0] - 1] = Math.min(newRoads[roads[i][1] - 1][roads[i][0] - 1], roads[i][2]);
}
return solve4(newRoads);
}
}
心之所向,素履以往 生如逆旅,一苇以航