网易校招编程题------最小难度唱歌

题目描述

这里写图片描述

思路分析

可以看出这是一道动态规划题
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();
    }
}

运行结果:
这里写图片描述

由边界条件不断推出后序的情况,最终获得最优解

这里写图片描述

猜你喜欢

转载自blog.csdn.net/DjokerMax/article/details/81747409