毕业旅行问题
毕业旅行问题
思路
首先容易想到的思路就是进行深度优先遍历,将所有可能的结果算出来求其最小值。但是这种算法明显时间复杂度过大,不在考虑范围内。
对于这种求最值的问题,我们一般想到用动态规划的方式求解。动态规划是将一个大的问题分解成几个小的问题。我们需要确定我们的dp数组所代表的含义,并找出递推公式。
对于这道题我们所要求的是从0号城市出发(即北京)经过其他所有城市(集合P)最终又回到0号城市的最小费用M;显然M等于Min{P中的一个城市City经过P-{City}城市最终又回到0号城市的最小费用+0号城市到City城市的费用}。于是我们可以定义dp[i][P] = Min{dp[j][P-j]+D}。其中P是城市集合。
我们该如何表示该集合,我们可以利用状态压缩的想法。将每一个城市作为一个二进制数的一位,一个int型数最多可以表示32个city.满足该题的最多20个城市。
dp[i][0]代表从i城市出发不经过其他城市直接到0号城市。其值为dp[i][0] = matrix[i][0]
我们最终的答案为dp[0][P]。其中P包含了除0号城市以外其他所有的城市。由上面所述,我们可以根据大集合的值是由小集合决定的。故第一层循环为循环所有的集合数。然后便是选取起点,再之后循环下一城市即可。
import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); int n = scanner.nextInt(); scanner.nextLine(); int[][] matrix = new int[n][n]; for(int i = 0 ; i < n; i++){ for(int j = 0; j < n; j++){ matrix[i][j] = scanner.nextInt(); } scanner.nextLine(); } int[][] dp = new int[n][1<<(n-1)]; for(int i = 0 ; i < n;i++){ dp[i][0] = matrix[i][0]; } //0不包含在dp数组第二维中,故i<1<<n-1 for(int i =1 ; i <(1<<(n-1));i++){ //选取起点 for(int j =0 ; j < n ;j++){ dp[j][i] = Integer.MAX_VALUE>>1; if(check(i,j)) continue; for(int k = 1 ;k<n;k++){ if(check(i,k)){ int tem = unmask(i,k); dp[j][i] = Math.min(dp[j][i],dp[k][tem]+matrix[j][k]); } } } } System.out.println(dp[0][(1<<(n-1))-1]); } public static boolean check(int a, int b){ if(b==0){ return false; } //如果等于0说明b不包含在a中。 return (a&(1<<(b-1)))!=0; } public static int unmask(int a,int b){ return a&(~(1<<(b-1))); } }