#P2839. 第2题-回文数组
          
                        
                                    
                      
        
              - 
          
          
                      1000ms
            
          
                      Tried: 73
            Accepted: 16
            Difficulty: 5
            
          
          
          
                       所属公司 : 
                              阿里
                                
            
                        
              时间 :2025年4月14日-阿里国际(开发岗)
                              
                      
          
 
- 
                        算法标签>贪心算法          
 
第2题-回文数组
题目分析
给定一个整数数组,我们需要通过一系列操作将其变成回文数组。每次操作可以选择两个相邻元素,增加它们的值。目标是计算至少需要多少次操作使得数组变成一个回文数组。如果不可能实现,则返回-1。
回文数组的定义是:该数组从左到右和从右到左完全相同。
思路
- 
对称性:要使一个数组成为回文数组,数组的左右两侧必须对称。即对于每一对元素,
a[i]和a[n-i-1],它们的值必须相等。 - 
操作方式:每次我们可以选择两个相邻元素增加其值。设定一个操作对两个相邻元素
a[i]和a[i+1]进行增加,即a[i] = a[i] + x和a[i+1] = a[i+1] + x,这样可以改变这些元素的大小,逐渐使其变得对称。 - 
策略:我们从数组的两端开始,逐步比较并调整不相等的元素。每次如果发现
a[i]和a[n-i-1]不相等,就将较小的元素增加,直到它们相等。这个操作的核心是通过最少的次数使得两边的元素逐渐接近。 - 
终止条件:如果两边的元素在经过若干次操作后能够相等,那么可以继续;否则如果无法通过加法使它们相等,那么就返回
-1。 - 
最优策略:每次操作都尽量增大最小的元素,这样可以保证最少的操作次数。
 
具体操作
- 从数组的两端开始,比较对称位置的元素。
 - 如果不相等,选择较小的元素进行加法操作,直到它们相等。
 - 记录每次操作的过程,并确保操作次数最小。
 
时间复杂度
由于我们需要一次遍历数组进行操作,时间复杂度为 O(n),其中 n 是数组的长度。每次操作只涉及两元素的加法,因此不会对算法的复杂度造成影响。
代码实现
Python 实现
def make_palindrome(n, arr):
    operations = []
    i, j = 0, n - 1
    
    while i < j:
        if arr[i] < arr[j]:
            diff = arr[j] - arr[i]
            arr[i] += diff
            arr[i + 1] += diff
            operations.append((i + 1, diff))  # 记录操作
        elif arr[i] > arr[j]:
            diff = arr[i] - arr[j]
            arr[j] += diff
            arr[j - 1] += diff
            operations.append((j, diff))  # 记录操作
        i += 1
        j -= 1
    
    # 检查是否是回文数组
    if arr == arr[::-1]:
        print(len(operations))
        for op in operations:
            print(op[0], op[1])
    else:
        print(-1)
# 输入读取
n = int(input())
arr = list(map(int, input().split()))
make_palindrome(n, arr)
Java 实现
import java.util.*;
public class Main {
    public static void makePalindrome(int n, long[] arr) {
        List<long[]> operations = new ArrayList<>();
        int i = 0, j = n - 1;
        
        while (i < j) {
            if (arr[i] < arr[j]) {
                long diff = arr[j] - arr[i];
                arr[i] += diff;
                arr[i + 1] += diff;
                operations.add(new long[]{i + 1, diff});
            } else if (arr[i] > arr[j]) {
                long diff = arr[i] - arr[j];
                arr[j] += diff;
                arr[j - 1] += diff;
                operations.add(new long[]{j, diff});
            }
            i++;
            j--;
        }
        
        // 检查是否是回文数组
        boolean isPalindrome = true;
        for (int k = 0; k < n / 2; k++) {
            if (arr[k] != arr[n - k - 1]) {
                isPalindrome = false;
                break;
            }
        }
        
        if (isPalindrome) {
            System.out.println(operations.size());
            for (long[] op : operations) {
                System.out.println(op[0] + " " + op[1]);
            }
        } else {
            System.out.println(-1);
        }
    }
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        long[] arr = new long[n];
        for (int i = 0; i < n; i++) {
            arr[i] = sc.nextLong();
        }
        makePalindrome(n, arr);
    }
}
C++ 实现
#include <iostream>
#include <vector>
using namespace std;
void makePalindrome(int n, vector<long long>& arr) {
    vector<pair<int, long long>> operations;
    int i = 0, j = n - 1;
    while (i < j) {
        if (arr[i] < arr[j]) {
            long long diff = arr[j] - arr[i];
            arr[i] += diff;
            arr[i + 1] += diff;
            operations.push_back({i + 1, diff});
        } else if (arr[i] > arr[j]) {
            long long diff = arr[i] - arr[j];
            arr[j] += diff;
            arr[j - 1] += diff;
            operations.push_back({j, diff});
        }
        i++;
        j--;
    }
    // 检查是否是回文数组
    bool isPalindrome = true;
    for (int k = 0; k < n / 2; k++) {
        if (arr[k] != arr[n - k - 1]) {
            isPalindrome = false;
            break;
        }
    }
    if (isPalindrome) {
        cout << operations.size() << endl;
        for (auto& op : operations) {
            cout << op.first << " " << op.second << endl;
        }
    } else {
        cout << -1 << endl;
    }
}
int main() {
    int n;
    cin >> n;
    vector<long long> arr(n);
    for (int i = 0; i < n; i++) {
        cin >> arr[i];
    }
    makePalindrome(n, arr);
    return 0;
}
        题目内容
小红有一个整数数组,长度为n。她希望通过一系列操作将数组变成一个回文数组。每次操作可以选择数组中任意两个相邻的元素ai和ai+1,将它们的值同时加一。
请你计算至少需要多少次操作使得数组变成一个回文数组。如果不可能,则输出−1。否则输出具体的操作方案。
注意:回文数组指该数组从左到右与从右到左完全相同。
输入描述
第一行包含一个正整数n(1≦n≦105),表示数组的长度。
第二行包含n个整数,表示数组的各个元素ai(1≦ai≦109)。
输出描述
如果不能通过操作使得数组变成回文数组,则输出−1。
否则先输出一个整数m,接下来m行,每行包含两个整数i和x,表示将a和ai+1同时加x,表示进行了x次操作。 你需要保证0≦m≦n,x>0,并且∑x最小
如果存在多个解决方案,您可以输出任意一个,系统会自动判定是否正确。注意,自测运行功能可能因此返回错误结果,请自行检查答案正确性。
样例1
输入
3
1 3 2
输出
1
1 1
说明
可以对数组的前两个元素进行一次操作,使1变为2,3变为4,此时数组变为[2,4,2],为回文数组。