LIS递增子序列o(nlogn)时间复杂度-马戏团人塔

有个马戏团正在设计叠罗汉的表演节目,一个人要站在另一人的肩膀上。出于实际和美观的考虑,在上面的人要比下面的人矮一点且轻一点。已知马戏团每个人的身高和体重,请编写代码计算叠罗汉最多能叠几个人。

思路:首先按照身高从低到高排序,然后找到对应体重的最长递增子序列。
注意身高和体重需要严格递增,不可以相等。

解法一: o(n^2)

class Solution {
    struct people{
        int height;
        int weight;
        bool operator <(const people & a)const{
            return height < a.height;
        }
    };
public:
    int bestSeqAtIndex(vector<int>& height, vector<int>& weight) {
       int len = height.size(), res = 0;
       vector<people>ini(len);
       for(int i = 0; i < len; i++){
           ini[i].height = height[i];

           ini[i].weight = weight[i];
       }
       sort(ini.begin(), ini.end());
       vector<int>dp(len, 1);
       for(int i = 0; i < len; i++){
           for(int j = i-1; j >= 0; j--){
           // 判断不能身高相同
               if(ini[i].height == ini[j].height)continue;
               if(ini[j].weight < ini[i].weight){
                   dp[i] = max(dp[i], dp[j] + 1);
               }
           }
           res = max(res, dp[i]);
       }
       return res;
    }
};

超时优化,做二分插入找到最长递增子序列。
解法二

class Solution {
    struct people{
        int height;
        int weight;
        bool operator <(const people & a)const{
        // 改进一
            if(height != a.height)return height < a.height;
            else return weight > a.weight;
        }
    };
public:
    int bestSeqAtIndex(vector<int>& height, vector<int>& weight) {
       int len = height.size(), res = 1;
       vector<people>ini(len);
       for(int i = 0; i < len; i++){
           ini[i].height = height[i];
           ini[i].weight = weight[i];
       }
       sort(ini.begin(), ini.end());
      
       vector<int> maxV(len+1);
       maxV[1] = ini[0].weight;
       // 改进二
       for(int i = 1; i < len; i++){
           if(ini[i].weight > maxV[res]){
               res++;
               maxV[res] = ini[i].weight;
           }else{
               int low = 1, high = res;
               while(low < high){
                   int mid = low + (high - low)/2;
                   if(ini[i].weight > maxV[mid]){
                       low = mid + 1;
                   }else{
                       high = mid;
                   }
               }
               maxV[low] = ini[i].weight;
           }
       }
       return res ;
    }

};

思考
改进一为什么要按照体重降序呢?
原因是改进二实现方式,维持一个完全递增的数组,其中maxV[i]表示长度为i的子序列的最小的结尾数字。如果当前数字很大,直接将maxV加长,否则去寻找大于等于当前数字的第一个位置,替换掉。
改进一的变化就是为了保证两个身高相同的人,他们的体重降序排列,不会再选择的时候这群身高相同的人只能被选中一个。

简洁写法

class Solution {
public:
    int bestSeqAtIndex(vector<int>& height, vector<int>& weight) {
        vector<pair<int,int>> tmp;
        for(int i = 0; i < height.size(); i++) tmp.push_back({height[i], weight[i]});
        sort(tmp.begin(), tmp.end(), [](const pair<int,int> &a, const pair<int,int> &b) {
            return a.first == b.first ? a.second > b.second : a.first < b.first;
        });
        vector<int> dp; //长度为N的地方 最小的数字
        for(const auto &[h, w]: tmp) {
            auto p = lower_bound(dp.begin(), dp.end(), w);  //二分查找第一个大于等于的地方
            if(p == dp.end()) dp.push_back(w);
            else *p = w;
        }
        return dp.size();
    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值