目录
写在前面:
刷到一道算法题,是网易校招的原题,不甚懂,打算看博客瞧瞧大佬们的思路
但由于本人较为愚钝,大佬们的博客写得太简略,我结合了许多篇,花了很长时间才看懂,
如果对这道题,你也有同样的困惑,恭喜刷到了这一篇博客。
这篇博客,将以我小白的观点啰嗦地理一理这道《合唱》
题目:
小Q和牛博士合唱一首歌曲,这首歌曲由n个音调组成,每个音调由一个正整数表示。
对于每个音调要么由小Q演唱要么由牛博士演唱,对于一系列音调演唱的难度等于所有相邻音调变化幅度之和, 例如一个音调序列是8, 8, 13, 12, 那么它的难度等于|8 - 8| + |13 - 8| + |12 - 13| = 6(其中||表示绝对值)。
现在要对把这n个音调分配给小Q或牛博士,让他们演唱的难度之和最小,请你算算最小的难度和是多少。
如样例所示: 小Q选择演唱{5, 6}难度为1, 牛博士选择演唱{1, 2, 1}难度为2,难度之和为3,这一个是最小难度和的方案了。
输入描述:
输入包括两行,第一行一个正整数n(1 ≤ n ≤ 2000) 第二行n个整数v[i](1 ≤ v[i] ≤ 10^6), 表示每个音调。
输出描述:
输出一个整数,表示小Q和牛博士演唱最小的难度和是多少。
示例1
输入
5 1 5 6 2 1
输出
3
思路总结:
首先要明确一个点,题目的意思是将一串音符的序列抽取出来,分成AB两个组,某个人演奏A组,另一个人个人演奏B组
也就是说,这两个人演奏的都是原字符数组里的互补的字符序列。
现在定义一个dp二位数组,(我们定义i始终大于j,也就是说,i这张二维表我们只用到一小半)。
dp[i][j]的意义为:现在已经演奏到了第i个音符,并且快的人以i音符结尾,慢的人演奏完了j音符,此时的最小难度代价的值为dp[i][j]
结果:结果存储Σ(dp[n-1][i])的最小值中,意思为更快的人已经演奏完了最后一个音符,此时音乐结束,通过for循环找音乐结束后时难度最小值
对于dp[i][j]
- 当i与j相邻,即i=j+1
- 说明此时发生了演奏的交换,即上一个音符是由演奏者A演奏,下一个音符换成了演奏者B演奏
- 那么此时dp[i][j] = 对所有k求min(dp[i-1][k] + abs(arr[i] - arr[k]) ) 其中(0
- 当i与j不相邻,即i>j+1
- 说明某个演奏家已经至少连续演奏了两个音符,所以dp[i][j]=dp[i-1][j]+abs(arr[i]-arr[i-1])
定义一个acc数组作为中间变量,acc[i]意思是,截止到如今,累加的音符差值和
acc[i]的作用:可以做为dp[i-1][j-1]的初始值,意思为前面所有的音符都由演奏家A演奏,最后一个音符由演奏家B演奏时的难度和。但是acc[i-1]不一定是dp[i][i-1]的最小值。
样例推导:
代码展示:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class Main{
public static void main(String[] args)throws IOException{
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
int n = Integer.parseInt(reader.readLine());
int array[] = new int[n];
if(n<3){
System.out.println(0);
return;
}
String[] str = reader.readLine().split(" ");
for(int i=0;i<n;i++){
array[i]=Integer.parseInt(str[i]);
}
//dp二位数组,dp[i][j]的意义为:现在已经演奏到了第i个音符,并且快的人以i音符结尾,慢的人演奏完了j音符,此时的最小难度代价的值为dp[i][j]
int dp[][] = new int[n][n];
//acc[i]意思是,截止到如今,累加的音符绝对值的和
int acc[] = new int[n];
//累加绝对值
for(int i=1;i<n;i++){
acc[i] = acc[i-1]+Math.abs(array[i]-array[i-1]);
}
for(int i=1;i<n;i++){
//acc[i]的作用:可以做为dp[i-1][j-1]的初始值,意思为前面所有的音符都由演奏家A演奏,最后一个音符由演奏家B演奏时的难度和。
//但是acc[i-1]不一定是dp[i][i-1]的最小值。
dp[i][i-1]=acc[i-1];
for(int j=0;j<i-1;j++){
//当i与j不相邻,即i>j+1。说明某个演奏家已经至少连续演奏了两个音符
dp[i][j]=dp[i-1][j] + Math.abs(array[i] - array[i-1]);
//说明此时发生了演奏的交换,即上一个音符是由演奏者A演奏,下一个音符换成了演奏者B演奏
//那么此时dp[i][j] = 对所有k求min(dp[i-1][k] + abs(arr[i] - arr[k]) ) 其中(0 <= k < i-1)
dp[i][i-1] = Math.min(dp[i][i-1], dp[i-1][j] + Math.abs(array[i]-array[j]));
}
}
int min = Integer.MAX_VALUE;
//结果存储Σ(dp[n-1][i])的最小值中,意思为更快的人已经演奏完了最后一个音符,说明音乐结束,找音乐结束后时难度最小值
//要注意j<n-1,一开始在定义时就要求i>j,dp[n-1][n-1]没有意义,因为不可能两个人同时演奏最后一个音符
for(int j=0;j<n-1;j++){
min=Math.min(min, dp[n-1][j]);
}
System.out.println(min);
}
}