leetcode 628: maximum3 product

原题 leetcode 628

如果选择3个最大数, 3是硬编码的, 那么其思路是

  • 基于排序: 对原数组排序, 然后取最大的3个正数, 最小的2个负数与最大的整数,求积,得到较大者; 由于基于比较的排序上线是O(nlogn)的, 基于排序的时间复杂度是O(nlogn);
  • 不排序: 类似于查找最大的k个数, 由于k一般远远小于n,可以认为时间复杂度是O(n)的, 可以利用声明k个元素的数组 + flags数组,来得到最大的3个数以及最小的2个负数; 其他思路一样;

对这道题扩展了一下要求, 若K是任意的? 上面的思路,不适用于K是任意的情况, 因为当K是任意的, 正数和负数的组合可能是穿插的;

隔了一天, 想到了第二个思路:

最大的k个数积: n个数中选任意3个, 求出积最大的那个, 实际上就等效于n个元素的集合选出k=3的子集, 然后遍历该子集求得到积的值最大的子集。代码求子集的方法可以用回溯法, 这个方法的思路和实现都很简洁;
给出下面的一个实现:

#include <iostream>
#include <vector>
#include <climits>

#define K 3
using namespace std;

class Solution {
public:
    int maximumProduct(vector<int>& nums) {
      
      robot(0, K, new vector<int>(), nums);
          
      int max = INT_MIN;
      for (auto item : output) {
        int one_result = 1;
        // cout << "one item: ";
        for (auto num : item) {
          //cout << num << " ";
          one_result *= num;  
        }   
        //cout << endl;
        //cout << "one_result=" << one_result << endl;
        max = std::max(max, one_result);
      }   
      //cout << "max=" << max << endl;
      return max;
    }   
 private:
  void robot(int idx, int k,  vector<int>* curr, vector<int>& nums);
  vector<vector<int>> output;
};

void Solution::robot(int idx, int k, vector<int>* curr, vector<int>& nums) {
  if (static_cast<int>(curr->size()) == k) {
    output.push_back(*curr);
    return; 
  }

  for (size_t i = idx; i < nums.size(); ++i) {
    curr->push_back(nums[i]);
    robot(i + 1, k, curr, nums);
    curr->pop_back();  //回溯
  }
}

这个方法可以解决部分输入样本, 但leetcode提供了一个10000个元素的样本, 出现了TLE;

针对这个问题,想到了一点优化方案:

先对整个集合排序,由于可能会有负数的存在,于是选取以下集合:

  • 尾部的k个最大的大于等于0的数;
  • 首部选取neg_count个可能的负数;
    neg_count是这样的数: 选集合中实际的负数的个数与k,选择2者较小者, 记为l, 找到最大的小于l的2的倍数个的数;
  • 以找到的k个正数和neg_count个负数的集合为集合,用上述的回溯法求出所有势为K的子集, 找出其中最大的积即可.

这样把,所有回溯法的样本空间的时间复杂度就被约束到了较小的空间, 如果K=3, 则回溯法的n=5;
这个回溯法robot的时间复杂度是很高的,类似N皇后问题, 这个时间复杂度是O(n!)级别的

这个思路的方法实现, 是可以通过的,代码如下:

#include <iostream>
#include <vector>
#include <climits>
#include <set>
#include <algorithm>

#define K 3
using namespace std;

class Solution {
public:
    int maximumProduct(vector<int>& nums) {

      std::sort(nums.begin(), nums.end());

      if(nums.size() == 1)
        return nums[0]; 
      if (nums.size() == 3) {
        return nums[0] * nums[1] * nums[2];
      }

      int count = K;
      vector<int> pos;
      for (auto ed = nums.end() - 1; ed >= nums.begin(); --ed) {
        if (*ed >= 0 && count > 0){ 
          pos.push_back(*ed);
          --count;  
        }
      }   
      vector<int> neg = findNegative(nums, K);    
      pos.insert(pos.end(), neg.begin(), neg.end());
      
      cout << "pos.size()=" << pos.size() << endl;
      robot(0, K, new vector<int>(), pos);

      int max = INT_MIN;
      for (auto item : output) {
        int one_result = 1;
        // cout << "one item: ";
        for (auto num : item) {
          // cout << num << " ";
          one_result *= num;
        }
        cout << endl;
        // cout << "one_result=" << one_result << endl;
        max = std::max(max, one_result);
      }
      // cout << "max=" << max << endl;
      return max;
    }
 private:
  void robot(int idx, int k,  vector<int>* curr, vector<int>& nums);
  vector<int> findNegative(const vector<int>& unqiue_nums, int k);
  vector<vector<int>> output;
};

void Solution::robot(int idx, int k, vector<int>* curr, vector<int>& nums) {
  if (static_cast<int>(curr->size()) == k) {
    output.push_back(*curr);
    return;
  }

  for (size_t i = idx; i < nums.size(); ++i) {
    curr->push_back(nums[i]);
    robot(i + 1, k, curr, nums);
    curr->pop_back();
  }
}

vector<int> Solution::findNegative(const vector<int>& unique_nums, int k) {
  int negative_count = 0;
  for (auto num : unique_nums)
    if (num < 0)
      negative_count++;

  int count = 1;
  int limit = std::min(k, negative_count);
  limit = limit % 2 == 0 ? limit : limit - 1;
  vector<int> result;
  for (int i = 0; i < limit; ++i) {
    result.push_back(unique_nums[i]);
  }
  return result;
}

另外, 参考一个k是3硬编码的实现:

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值