1. 程式人生 > 其它 >旅行商問題

旅行商問題

連結

給 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);
    }
}
心之所向,素履以往 生如逆旅,一葦以航