题目描述
思路分析
可以看出这是一道动态规划题
1. dp[i][j](假设一直有i > j)表示某一个人最近唱的音为第i个,另一个人最近唱的是第j个时最小的难度,由于只由一个人唱完肯定不是最优解。因此先在一个for循环内确定以下两种情况的初值
(1)dp[i][0]:第二个人唱第一个音,第一个人唱后面所有音
(2)dp[i][i-1]:第一个人唱最近的一个音,第二个人唱前面所有音
2. dp[i][j]转移方程
每当交换唱歌次序,两人最近一次唱的音符一定是相邻的,所以分相邻和不相邻讨论:
(1). 当j == i - 1,即交换唱歌次序,dp[i][i-1]时,表明第一个人上一个音可能在第k个音(0 <= k < i-1),由唱了最近的第i个,第二个人仍然留在第i-1个音。dp[i][i-1] 等于对所有k求min(dp[i-1][k] + abs(arr[i] - arr[k]) ) 其中(0 <= k < i-1)
(2). 当j < i - 1,即不交换唱歌次序时,只可能由唱到i-1音符的人续唱,dp[i][j] = dp[i-1][j] + abs(arr[i] - arr[i-1])
3. 最后求出所有dp[len-1][]里的最小值即为全局最优解
实例代码
package com.zhumq.lianxi;
import java.util.Scanner;
public class SingDP {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
while (in.hasNext()) {
int len = in.nextInt();
int[] arr = new int[len];
for (int i = 0; i < len; ++i) {
arr[i] = in.nextInt();
}
if (len < 3) {
System.out.println("0");
} else {
int[][] dp = new int[len][len];
int[] acc = new int[len];
dp[0][0] = 0 - Math.abs(arr[1] - arr[0]);
for (int i = 1; i < len; ++i) {
//当前后相邻时,说明了换人
//边界情况:dp[i][0]第二个人唱了第一个音,第一个人唱i-1个音
//边界情况:dp[i][i-1]第一个唱最近的一个音,第二个人唱后面所以的音
acc[i] = acc[i - 1] + Math.abs(arr[i] - arr[i - 1]);
dp[i][i - 1] = acc[i - 1];
for (int j = 0; j < i - 1; ++j) {
//当不相邻时就直接有dp[i-1][j]的上一个人接着唱
dp[i][j] = dp[i - 1][j] + acc[i] - acc[i - 1];
//一人唱完之后,等另一个人唱完再唱还要计算从本次位置到上次结束位置的距离
//不断遍历到其他情况并更新最小值
dp[i][i - 1] = Math.min(dp[i][i - 1], dp[i - 1][j] + Math.abs(arr[i] - arr[j]));
System.out.println("dp["+i+"]["+j+"]:"+dp[i][j] + " " + "dp["+i+"]["+(i-1)+"]:"+dp[i][i-1]);
}
}
int min = Integer.MAX_VALUE;
for (int j = 0; j < len - 1; ++j) {
min = Math.min(min, dp[len - 1][j]);
}
System.out.println(min);
}
}
in.close();
}
}
运行结果: