1 条题解

  • 0
    @ 2025-9-14 18:54:10

    题解思路

    这是一道典型的二分答案+贪心策略验证问题,核心在于将最小化最大值问题转化为判定性问题,通过双向约束传播实现最优解。

    核心算法分析

    问题转换思路: 原问题要求最小化相邻差的最大值,这是经典的最小化最大值优化问题。我们可以转换为判定问题:给定阈值X,能否通过k次操作使所有相邻差都不超过X?

    关键洞察: 对于给定的X,要使|a[i+1] - a[i]| ≤ X,等价于: a[i]−X≤a[i+1]≤a[i]+Xa[i] - X \leq a[i+1] \leq a[i] + X

    双向约束传播策略

    前向传播:确保右侧元素满足左侧约束

    for i in range(1, n):
        if b[i] < b[i-1] - X:
            b[i] = b[i-1] - X  // 右侧至少要达到这个下界
    

    后向传播:确保左侧元素满足右侧约束

    for i in range(n-2, -1, -1):
        if b[i] < b[i+1] - X:
            b[i] = b[i+1] - X  // 左侧至少要达到这个下界
    

    算法正确性证明

    1. 单调性:X越大,约束越宽松,所需操作次数单调递减
    2. 最优性:双向传播确保了在满足约束条件下的最小增长
    3. 完整性:两次扫描保证了所有相邻约束都被满足

    详细算法设计

    二分搜索框架

    private static boolean canAchieve(int[] array, int maxDiff, long maxOps) {
        int n = array.length;
        long[] modified = new long[n];
        
        // 复制原数组(使用long防止溢出)
        for (int i = 0; i < n; i++) {
            modified[i] = array[i];
        }
        
        // 前向约束传播
        for (int i = 1; i < n; i++) {
            if (modified[i] < modified[i-1] - maxDiff) {
                modified[i] = modified[i-1] - maxDiff;
            }
        }
        
        // 后向约束传播
        for (int i = n-2; i >= 0; i--) {
            if (modified[i] < modified[i+1] - maxDiff) {
                modified[i] = modified[i+1] - maxDiff;
            }
        }
        
        // 计算总操作次数
        long totalOps = 0;
        for (int i = 0; i < n; i++) {
            totalOps += modified[i] - array[i];
            if (totalOps > maxOps) return false; // 提前终止
        }
        
        return true;
    }
    

    复杂度分析

    • 单次验证:O(n)
    • 二分搜索:O(log(max_diff)),其中max_diff为初始最大相邻差
    • 总体复杂度:O(n log(max_diff))
    • 空间复杂度:O(n)

    Java代码实现

    import java.io.*;
    import java.util.*;
    
    public class Main {
        
        private static long calculateRequiredOperations(int[] originalArray, int targetMaxDiff) {
            int length = originalArray.length;
            long[] adjustedArray = new long[length];
            
            // 初始化调整后的数组
            for (int i = 0; i < length; i++) {
                adjustedArray[i] = originalArray[i];
            }
            
            // 前向传播:保证右侧元素满足左侧约束
            for (int i = 1; i < length; i++) {
                long minimumRequired = adjustedArray[i-1] - targetMaxDiff;
                if (adjustedArray[i] < minimumRequired) {
                    adjustedArray[i] = minimumRequired;
                }
            }
            
            // 后向传播:保证左侧元素满足右侧约束
            for (int i = length - 2; i >= 0; i--) {
                long minimumRequired = adjustedArray[i+1] - targetMaxDiff;
                if (adjustedArray[i] < minimumRequired) {
                    adjustedArray[i] = minimumRequired;
                }
            }
            
            // 计算所需的总操作次数
            long totalOperations = 0;
            for (int i = 0; i < length; i++) {
                totalOperations += adjustedArray[i] - originalArray[i];
            }
            
            return totalOperations;
        }
        
        private static boolean isAchievable(int[] array, int maxDifferenceLimit, long availableOperations) {
            return calculateRequiredOperations(array, maxDifferenceLimit) <= availableOperations;
        }
        
        private static int findMinimumMaxDifference(int[] array, long maxOperations) {
            int n = array.length;
            
            // 确定二分搜索的右边界:初始最大相邻差
            int rightBound = 0;
            for (int i = 0; i < n - 1; i++) {
                int currentDiff = Math.abs(array[i+1] - array[i]);
                rightBound = Math.max(rightBound, currentDiff);
            }
            
            int leftBound = 0;
            
            // 二分搜索最小可能的最大相邻差
            while (leftBound < rightBound) {
                int midValue = leftBound + (rightBound - leftBound) / 2;
                
                if (isAchievable(array, midValue, maxOperations)) {
                    rightBound = midValue;
                } else {
                    leftBound = midValue + 1;
                }
            }
            
            return leftBound;
        }
        
        public static void main(String[] args) throws IOException {
            BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
            int testCases = Integer.parseInt(reader.readLine());
            
            StringBuilder resultBuilder = new StringBuilder();
            
            for (int t = 0; t < testCases; t++) {
                String[] parameters = reader.readLine().split(" ");
                int n = Integer.parseInt(parameters[0]);
                long k = Long.parseLong(parameters[1]);
                
                String[] arrayElements = reader.readLine().split(" ");
                int[] inputArray = new int[n];
                
                for (int i = 0; i < n; i++) {
                    inputArray[i] = Integer.parseInt(arrayElements[i]);
                }
                
                int result = findMinimumMaxDifference(inputArray, k);
                resultBuilder.append(result).append('\n');
            }
            
            System.out.print(resultBuilder.toString());
            reader.close();
        }
    }
    

    C++代码实现

    #include <iostream>
    #include <vector>
    #include <algorithm>
    #include <climits>
    using namespace std;
    
    class AdjacentDifferenceOptimizer {
    private:
        static long long computeMinOperations(const vector<int>& original, int maxAllowedDiff) {
            int size = original.size();
            vector<long long> modified(size);
            
            // 复制原始数组到long long类型以防溢出
            for (int i = 0; i < size; i++) {
                modified[i] = original[i];
            }
            
            // 正向扫描:确保每个元素满足左邻居的约束
            for (int i = 1; i < size; i++) {
                long long lowerBound = modified[i-1] - maxAllowedDiff;
                if (modified[i] < lowerBound) {
                    modified[i] = lowerBound;
                }
            }
            
            // 反向扫描:确保每个元素满足右邻居的约束
            for (int i = size - 2; i >= 0; i--) {
                long long lowerBound = modified[i+1] - maxAllowedDiff;
                if (modified[i] < lowerBound) {
                    modified[i] = lowerBound;
                }
            }
            
            // 统计总的增量操作
            long long totalIncrements = 0;
            for (int i = 0; i < size; i++) {
                totalIncrements += modified[i] - original[i];
            }
            
            return totalIncrements;
        }
        
    public:
        static int solveMinMaxDifference(const vector<int>& array, long long maxOperations) {
            int arraySize = array.size();
            
            // 计算初始状态下的最大相邻差作为搜索上界
            int searchUpperBound = 0;
            for (int i = 0; i < arraySize - 1; i++) {
                int adjacentDiff = abs(array[i+1] - array[i]);
                searchUpperBound = max(searchUpperBound, adjacentDiff);
            }
            
            int searchLowerBound = 0;
            
            // 使用二分搜索寻找最优解
            while (searchLowerBound < searchUpperBound) {
                int candidateAnswer = searchLowerBound + (searchUpperBound - searchLowerBound) / 2;
                
                long long requiredOps = computeMinOperations(array, candidateAnswer);
                
                if (requiredOps <= maxOperations) {
                    searchUpperBound = candidateAnswer;
                } else {
                    searchLowerBound = candidateAnswer + 1;
                }
            }
            
            return searchLowerBound;
        }
    };
    
    int main() {
        ios_base::sync_with_stdio(false);
        cin.tie(nullptr);
        
        int numberOfTests;
        cin >> numberOfTests;
        
        while (numberOfTests--) {
            int n;
            long long k;
            cin >> n >> k;
            
            vector<int> inputSequence(n);
            for (int i = 0; i < n; i++) {
                cin >> inputSequence[i];
            }
            
            int optimalResult = AdjacentDifferenceOptimizer::solveMinMaxDifference(inputSequence, k);
            cout << optimalResult << '\n';
        }
        
        return 0;
    }
    

    时间复杂度:O(n log(max_diff)),其中max_diff为初始最大相邻差 空间复杂度:O(n),用于存储临时调整数组

    • 1

    信息

    ID
    292
    时间
    1000ms
    内存
    256MiB
    难度
    6
    标签
    递交数
    1
    已通过
    1
    上传者