首页 > 试题广场 >

回文数组

[编程题]回文数组
  • 热度指数:6253 时间限制:C/C++ 3秒,其他语言6秒 空间限制:C/C++ 64M,其他语言128M
  • 算法知识视频讲解
对于一个给定的正整数组成的数组 a[] ,如果将 a 倒序后数字的排列与 a 完全相同,我们称这个数组为“回文”的。
例如, [1, 2, 3, 2, 1] 的倒序是他自己,所以是一个回文的数组;而 [1, 2, 3, 1, 2] 的倒序是 [2, 1, 3, 2, 1] ,所以不是一个回文的数组。
对于任意一个正整数数组,如果我们向其中某些特定的位置插入一些正整数,那么我们总是能构造出一个回文的数组。

输入一个正整数组成的数组,要求你插入一些数字,使其变为回文的数组,且数组中所有数字的和尽可能小。输出这个插入后数组中元素的和。

例如,对于数组 [1, 2, 3, 1, 2] 我们可以插入两个 1 将其变为回文的数组 [1, 2, 1, 3, 1, 2, 1] ,这种变换方式数组的总和最小,为 11 ,所以输出为 11 。

输入描述:
输入数据由两行组成: 第一行包含一个正整数 L ,表示数组 a 的长度。 第二行包含 L 个正整数,表示数组 a 。  对于 40% 的数据: 1 < L <= 100 达成条件时需要插入的数字数量不多于 2 个。  对于 100% 的数据: 1 < L <= 1,000 0 < a[i] <= 1,000,000 达成条件时需要插入的数字数量没有限制。


输出描述:
输出一个整数,表示通过插入若干个正整数使数组 a 回文后,数组 a 的数字和的最小值。
示例1

输入

8
51 23 52 97 97 76 23 51

输出

598

状态转移公式很容易想出来
规定dp[i][j]为数组nums[i:j+1](Go数组切片表示法,不包含结束位置)的最小和。

  • 当nums[i] == nums[j]时,是两边的数字加上中间的数组nums[i+1:j]的最小和:dp[i][j] = dp[i+1][j-1] + 2 * nums[i]。
  • 当nums[i] != nums[j]时,有两种情况
    • 加入nums[i],因此最终结果为数组nums[i+1][j+1]的最小和以及左边数字的两倍,两倍是因为加入了一个nums[i],加上本来的nums[i]。
    • 加入nums[j]同理
    • 这两种情况取最小即可

但是还没完,还需要思考一个问题,哪里是回文数组的中心?

给定例子里面看到,好像两边都有一些同样的数字,我一开始也想着就是应该是数组中间附近是中心吧。但真的不一定是,如果给定的数组是混乱的,那么回文数组中心是不确定的。因此,我们需要以任意位置作为回文数组的中心来尝试。

也就是说,我们需要遍历所有的情况,算出所有的dp[i][j](0 <= i <= j < nums.length)。

代码如下,采用了自底向上方法实现的动态规划。

import java.util.*;
public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int[] nums = new int[n];
        for (int i = 0; i < n; i++) {
            nums[i] = in.nextInt();
        }
        System.out.println(solve(nums));
    }
    public static int solve(int[] nums) {
        int[][] dp = new int[nums.length][nums.length];
        for (int i = nums.length - 1; i >= 0; i--) {
            dp[i][i] = nums[i];
            for (int j = i + 1; j < nums.length; j++) {
                if (nums[j] == nums[i]) {
                    dp[i][j] = dp[i + 1][j - 1] + 2 * nums[j];
                } else {
                    int left = dp[i + 1][j] + 2 * nums[i];
                    int right =  dp[i][j - 1] + 2 * nums[j];
                    dp[i][j] = left < right ? left : right;
                }
            }
        }
        return dp[0][nums.length - 1];
    }
}
编辑于 2019-12-25 19:53:48 回复(2)