题解 | #牛牛的消消乐#

牛牛的消消乐

http://www.nowcoder.com/practice/9deb03b935ec4fd288a8ee5d20364581

思路:

题目的主要信息:

  • 对一个数组,每次选1个元素进行操作,两次操作后使数组和最小
  • 操作为:任选一个元素x,将数组中所有大于等于x的数减去x

方法一:暴力法
具体做法:
首先对数组进行排序,使之成为递增顺序。求一个数组的和sum,然后就是找到sum要减去的最大值。这个最大值就是进行两次操作的时候要减去的数的总和。
因为数组是递增次数,因此我们如果要减去的元素是,那么从下标全部都可以减去,减去的部分就是,因此我们可以遍历每个元素,减去1个之后,再对其排序再遍历减去第二个元素,找到能够减去的最大值。
最后sum减去最大值就是所求结果。

class Solution {
public:
    long long minimumValueAfterDispel(vector<int>& nums) {
        long long sum = 0;
        int n = nums.size();
        for(int i = 0; i < n; i++)
            sum += nums[i];
        vector<int> temp = nums; //辅助数组,只在辅助数组上减
        sort(nums.begin(), nums.end()); //排序
        int max = 0;
        for(int i = 0; i < n; i++){
            int delete1 = nums[i] * (n - i); //减去第一个数
            for(int j = i; j < n; j++)
                temp[j] -= nums[i];
            sort(temp.begin(), temp.end()); //减去之后排序
            for(int j = 0; j < n; j++){
                int delete2 = temp[j] * (n - j);  //减去第二个数
                max = max > delete1 + delete2 ? max : delete1 + delete2; //维护最大值
            }
            temp = nums; //辅助数组回归原来的样子
        }
        return sum - max;
    }
};

复杂度分析:

  • 时间复杂度:,第一个排序为,与后面的比较起来可忽略;第一层遍历是,循环里面有三个(分别是减去第一个数、找到减去第二个数的最大值、回归辅助数组),循环里面还有一个sort排序,故最终的结果是
  • 空间复杂度:,辅助数组temp

方法二:数学分段法
具体做法:
首先将数组排成增序。
遍历的数组中的任意两个数,我们可以假设两次减去的数分别为,那么可以分为三种情况:

  1.      
  2.      
  3.    

与之对应的定义域函数分段:

对于情况1:中的数只能减, 中的数只能减, 中的数可以减去,即
对于情况2:中的数只能减, 之间的数只能减, 中的数可以才减,即
对于情况3:中的数只能减, 中的数只能减, 中的数才可以减,即

图片说明
因此我们遍历两层数组,找到三个边界值index1 index2 index3,然后用区间长度乘上减去的数即可,寻炸这三种情况的最大值,用总和减去最大值即可得到结果。
(以下代码因为遍历顺序问题,i与j与上述相反)

class Solution {
public:
    long long minimumValueAfterDispel(vector<int>& nums) {
        long long sum = 0;
        long long max = 0;
        int n = nums.size();
        sort(nums.begin(), nums.end());//排序
        vector<long long> numl; //将int型数组全部转换成long long,方便运算
        for(int i = 0; i < n; i++)
            numl.push_back((long long)nums[i]);
        for(int i = 0; i < n; i++){
            sum += numl[i]; //求和
            int index1 = i, index2 = i, index3 = i;
            for(int j = 0; j <= i; j++){
                //找区间边界
                while(index1 > 0 && numl[index1 - 1] >= numl[i] - numl[j])
                    index1--;
                while(index2 > i && numl[index2 - 1] >= numl[j] - numl[i])
                    index2--;
                while(index3 < n && numl[index3] < numl[j] + numl[i])
                    index3++;
                //三种情况减去的总和
                long long temp1 = (j - index1) * (numl[i] - numl[j]) + (i - j) * numl[j] + (n - i) * numl[i];
                long long temp2 = (index2 - j) * numl[j] + (i - index2) * (numl[i] - numl[j]) + (n - i) * numl[i];
                long long temp3 = (i - j) * numl[j] + (index3 - i) * numl[i] + (n - index3) * (numl[j] + numl[i]);
                max = max > temp1 ? max : temp1;//维护最大值
                max = max > temp2 ? max : temp2;
                max = max > temp3 ? max : temp3;
            }        
        }
        return sum - max;
    }
};

复杂度分析:

  • 时间复杂度:,两层循环加一个在外的可忽略的快排和一个遍历赋值
  • 空间复杂度:,用long long表示的数组numl
孤帆远影碧空尽 文章被收录于专栏

牛客网各类题单题解~

全部评论

相关推荐

2 收藏 评论
分享
牛客网
牛客企业服务