字節跳動真題:用動態規划解旅行商問題


小明目前在做一份畢業旅行的規划。打算從北京出發,分別去若干個城市,然后再回到北京,每個城市之間均乘坐高鐵,且每個城市只去一次。由於經費有限,希望能夠通過合理的路線安排盡可能的省一些路上的花銷。給定一組城市和每對城市之間的火車票的價錢,找到每個城市只訪問一次並返回起點的最小車費花銷。

輸入描述:

城市個數n(1<n≤20,包括北京, 北京為城市0)

城市間的車票價錢 n行n列的矩陣 m[n][n]

輸出描述:

最小車費花銷 s

示例:

輸入:

4
0 2 6 5
2 0 4 4
6 4 0 2
5 4 2 0

輸出:

13

思路:

dp[0][{1,2,3}]表示從城市0出發,經過城市1,2,3並返回城市0需要的最少車費,那么該問題可以分解為:

dp[0][{1,2,3}] = min(cost(0,1)+ dp[1][{2,3}], cost(0,2) + dp[2][{1,3}], cost(0,3) + dp[3][{1,2}]),表示先從城市0到城市1,在從城市1出發,經過城市2,3並返回城市0的車費、

先從城市0到城市2,在從城市2出發,經過城市1,3並返回城市0的車費、先從城市0到城市3,在從城市3出發,經過城市1,2並返回城市0的車費,這3個里的最小值就是最后的答案。

把思路轉化成代碼需要解決兩個問題:

1.在dp數組中{1,2,3}這種城市的集合怎么表示

2.在選好出發城市后,怎么把這和城市在城市集合中刪除

針對問題1:對城市集合用二進制編碼,{1,2,3}表示為111=7,{2,3}表示為110=6,{1,3}表示為101=5,以此類推

針對問題2:根據問題1的編碼方式,刪除一個城市就是把它在二進制編碼中對應的位置從1變成0,可以通過把1左移對應的位數,然后做異或實現

代碼:

import java.util.*;
public class Main{
    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int[][] c = new int[n][n];
        for(int i = 0; i < n; i++){
            for(int j = 0; j < n; j++){
                c[i][j] = sc.nextInt();
            }
        }
        int v = 1 << (n - 1);    // 城市集合二進制編碼的范圍是0-(1 << (n - 1)) - 1
        int[][] dp = new int[n][v];
        for(int i = 0; i < n; i++){
            dp[i][0] = c[i][0];          //  base case,從城市i出發,不經過其他城市,最后返回城市0,那最少車費就是i到0的費用
        }
        for(int j = 1; j < v; j++ ){             // 注意,這里的遍歷方向是豎着遍歷的,因為根據動態規划的數組遍歷原則,遍歷的終點是最后的答案,而這道題終點是dp[0][v-1],也就是數組的右上角,而且在v-1這列,只有第0行會存值,所以可以對每一列從上到下遍歷
            for(int i = 0; i < n; i++){
                if(((j >> (i - 1)) & 1) == 0){     // 起點城市不在需要的城市集合里才能繼續,因為對於dp[1][{1,2,3}]表示從1出發,經過1,2,3后回到0,城市1被重復經過,不符合題目要求
                    dp[i][j] = Integer.MAX_VALUE;
                    for(int k = 1; k < n; k++){
                        if(((j >> (k - 1)) & 1) == 1){    //  表示要選的新的起點,在城市集合里
                            dp[i][j] = Math.min(dp[i][j], c[i][k] + dp[k][j ^ (1 << (k - 1))]);
                        }
                    }
                }
            }
        }
        System.out.println(dp[0][v - 1]);
    }
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM