1 条题解
-
0
题解思路
这是一道典型的二分答案+贪心策略验证问题,核心在于将最小化最大值问题转化为判定性问题,通过双向约束传播实现最优解。
核心算法分析
问题转换思路: 原问题要求最小化相邻差的最大值,这是经典的最小化最大值优化问题。我们可以转换为判定问题:给定阈值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 // 左侧至少要达到这个下界
算法正确性证明:
- 单调性:X越大,约束越宽松,所需操作次数单调递减
- 最优性:双向传播确保了在满足约束条件下的最小增长
- 完整性:两次扫描保证了所有相邻约束都被满足
详细算法设计
二分搜索框架:
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),用于存储临时调整数组
信息
- ID
- 292
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 6
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者