算法思想一:贪心
解题思路:
利用贪心思想,利用一个辅助数组每次操作前记录相邻两个数组之差,并找到差的最大值的下标。这种时候我们有两种选择:较大的那个数减少,或者较小的那个数增大,每次操作时需要盘旋小数和大数谁在前,需要判断边界,需要判断前后一个数更改谁使最大值最小。
dp记录相邻两个数组之差的辅助数组:
图解:
代码展示:
C++版本
class Solution { public: /** * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可 * * 取球放球 * @param n int整型 箱子个数 * @param k int整型 最多操作次数 * @param a int整型vector 箱子内的球的个数 * @param w int整型vector 箱子容量 * @return int整型 */ int solve(int n, int k, vector<int>& a, vector<int>& w) { // write code here for(int i = 0; i < k; i++){ //k次调整 //每次调整前计算当前相邻差平方值最大的下标 vector dp(n - 1, 0); //记录相邻两个数组之差的辅助数组 int index = -1; int maxdis = 0; for(int i = 0; i < n - 1; i++){ dp[i] = a[i + 1] - a[i]; if(dp[i] * dp[i] > maxdis){ //找最大 maxdis = dp[i] * dp[i]; index = i; } } if(index == -1) //没找到 continue; if(dp[index] > 0){ //选择前面的数增大或后面的数减少 if(a[index] == w[index]){//比较容量,若是较小的数无法增大,则后面减少 a[index + 1]--; continue; } if(index == n - 2){ //若为最后两个数 if(index - 1 >= 0 && dp[index - 1] < 0) a[index]++; else a[index + 1]--; } else if(index == 0){ //若为前两个数 if(index + 1 <= n - 2 && dp[index + 1] < 0) a[index + 1]--; else a[index]++; } else{ //中间两个数 if(dp[index + 1] < 0) //后面一组是减少的 直接index+1减少 a[index + 1]--; else{ if(dp[index - 1] < 0) //前面一组是减少的,直接index-1增加 a[index]++; else{ //三组都是增大的 if(dp[index - 1] < dp[index + 1]) //前面那组的增大趋势更大 a[index + 1]--; else a[index]++; } } } } else{ //选择前面的数减少或后面的数增大 if(a[index + 1] == w[index + 1]){//若是较小的数无法增大,则后面减少 a[index]--; continue; } if(index == n - 2){ //若是最后两个数 if(index - 1 >= 0 && dp[index - 1] > 0) a[index]--; else a[index + 1]++; } else if(index == 0){ //若是前两个数 if(index + 1 <= n - 2 && dp[index + 1] < 0) a[index]--; else a[index + 1]++; }else{ //比较相邻三组 if(dp[index + 1] > 0) a[index + 1]++; else{ if(dp[index - 1] > 0) a[index]--; else{ //三组都是减少的 if(dp[index - 1] > dp[index + 1]) a[index]--; else a[index + 1]++; } } } } } int res = 0; for(int i = 0; i < n - 1; i++) res = max(res, (a[i + 1] - a[i]) * (a[i + 1] - a[i])); //找到最大值 return res; } };
复杂度分析
时间复杂度:k次操作,每次操作只有一个循环
空间复杂度:dp数组占用空间
算法思想二:动态规划+二分
解题思路:
对于数组每次都要找到相邻元素之差的平方的最大值,最终任务是最小化这个最大值,因此可以考虑用二分查找来找这个。
1、首先用 表示第 i 个箱子的球的个数为 j 且前 i 个箱子中相邻箱子差的绝对值都不超过 mid(二分法每次的中间值)最少需要的操作次数,那么第 i+1 个箱子最少有的球的公式为:,最多有的球的个数为:。
2、根据最小的操作次数和题目给出的 k 来改变二分边界,因为dp数组中 就是所有箱子中相邻差不超过 mid 的最小操作次数,要找的就是其中的最小值,如果这个数字小于 k ,边界左移,缩小查找范围,如果大于 k,边界右移
代码展示:
C++版本
class Solution { public: int solve(int n, int k, vector<int>& a, vector<int>& w) { int l = 0, r = 500; int res = 0; int dp[101][501]; while(l <= r){ //二分法检查相邻元素的绝对值全部是否小于等于mid int mid = (l + r) / 2; memset(dp, 0x3f, sizeof(dp)); //每次都要初始化 for(int i = 0; i <= w[0]; i++) dp[0][i] = abs(a[0] - i); for(int i = 0; i < n - 1; i++){ for(int j = 0; j <= w[i]; j++){ int mn = max(0, j - mid); int mx = min(j + mid, w[i + 1]); for(int z = mn; z <= mx; z++) //从最小到最大更新dp dp[i + 1][z] = min(dp[i + 1][z], dp[i][j] + abs(a[i + 1] - z)); } } int now = dp[n - 1][0]; //更新 for(int i = 1; i <= w[n - 1]; i++) now = min(now, dp[n - 1][i]); if(now <= k){ res = mid; r = mid - 1; } else l = mid + 1; } return res * res; } };
复杂度分析
时间复杂度:二分查找时间,再加三层循环时间
空间复杂度:dp数组占用空间