字节跳动真题:用动态规划解旅行商问题


小明目前在做一份毕业旅行的规划。打算从北京出发,分别去若干个城市,然后再回到北京,每个城市之间均乘坐高铁,且每个城市只去一次。由于经费有限,希望能够通过合理的路线安排尽可能的省一些路上的花销。给定一组城市和每对城市之间的火车票的价钱,找到每个城市只访问一次并返回起点的最小车费花销。

输入描述:

城市个数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