题目描述 Description
某特种部队接到一个任务,需要潜入一个仓库。该部队士兵分为两路,第一路士兵已经在正面牵制住了敌人,第二路士兵正在悄悄地从后方秘密潜入敌人的仓库。
当他们到达仓库时候,发现这个仓库的锁是一把很诡异的电子锁,上面是一排按钮,每个按钮上都有一个数字。10 秒钟后,总部返回了该锁的技术信息。要解开这把锁,首先要从左边的第一个按钮开始向右按动,中间可以跳过某些按钮,按动到最右边的按钮后,反向向左按动。最终,每个按钮都要按且仅按一次。每两个相邻按钮上数字之差的总和的最小值,便是解开这把锁的密码。
作为一支装备精良的特种部队,必须要在最短的时间内完成任务,解开这把锁,潜入仓库。
输入描述 Input Description
第一行是一个n(2 <= n <= 1000)表示共有n 个按钮。
第二行是n 个正整数,代表从左至右各按钮上的数字,数值均不超过2000。
输出描述 Output Description
只有一个数,为这把锁的密码。
样例输入 Sample Input
5
1 2 3 4 5
样例输出 Sample Output
4
数据范围及提示 Data Size & Hint
2 <= n <= 1000,数值不超过2000
分析:
动态规划的题目。
可以将它看作是两条路同时从最后的那个点往回走(即两条路起点相同,终点不同,且除了最后一点之外,两条路不能经过相同的一点),因此为了便于计算和理解,我们在最初存储数组的时候倒着存储数据,这样两条路都从最前面开始了。
利用二维数组dp[i][j]来进行动归求解,dp[i][j]碉堡两条路分别到达i和j( i > j ),并且从1至i的所有点都经过了,为了更加直观一点,我们假设两条路分别为A和B,假设在一个状态下,A走到i,B 走到 j ,则可以推断出下面状态:
状态1:A上一步就在B的前面(此时B的位置 < i-1),现在A又往前面走了一步(从 i-1 走到 i)。
状态2:A上一步在B的后面(此时A所在的位置 < i-1), 因为i>j,则B在 i -1 位置不变,这一步A超过了B(从 < i-1 走到 i )
代码如下:
public class DPTesr{
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入按钮数量:");
int n = scanner.nextInt();
System.out.println("请输入各按钮数字(数字间空格隔开):");
scanner.nextLine();
String[] numStr = scanner.nextLine().trim().split(" ");
int[] numArr = new int[n+1];
//倒着存储
for (int i = n; i > 0; i--){
numArr[i] = Integer.parseInt(numStr[i-1]);
}
scanner.close();
//状态存储
int[][] dp = new int[n+1][n+1];
//初始化
for (int i = 0; i <= n; i++){
for (int j = 0; j <= n; j++){
dp[i][j] = 0x3f3f3f3f;
}
}
dp[1][1] = 0;
dp[1][2] = Math.abs(numArr[2]-numArr[1]);
dp[2][1] = Math.abs(numArr[2]-numArr[1]);
int ans = 0x3f3f3f3f;
//从3开始j能存在
for (int i = 3; i <= n; i++){
for (int j = 1; j < i-1; j++) {
//上一步A走在前面的继续往前走的场景(i>j)
dp[i][j] = Math.min(dp[i][j], dp[i-1][j]+Math.abs(numArr[i]-numArr[i-1]));
//上一步B走在前面的继续往前走的场景(i>j)
dp[j][i] = Math.min(dp[j][i], dp[j][i-1]+Math.abs(numArr[i]-numArr[i-1]));
//上一步B落后在后面的超过前面A的场景(1到i到点都走过了,在B落后超前都场景中,超前后的位置A在i-1位置,B则在i的位置)
dp[i-1][i] = Math.min(dp[i-1][i], dp[j][i-1]+Math.abs(numArr[i]-numArr[j]));
//上一步A落后在后面的超过前面的B场景(1到i到点都走过了,在A落后超前都场景中,超前后的位置B在i-1位置,超前后A则在i的位置)
dp[i][i-1] = Math.min(dp[i][i-1], dp[i-1][j]+Math.abs(numArr[i]-numArr[j]));
}
}
//只要有其中一个走到末尾就可以得到答案
for (int k = 1; k <= n; k++){
ans = Math.min(ans, dp[k][n]);
}
System.out.println(ans);
}
}