【程序员面试金典】面试题 17.08. 马戏团人塔

【程序员面试金典】面试题 17.08. 马戏团人塔

题目描述

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

示例:

输入:height = [65,70,56,75,60,68] weight = [100,150,90,190,95,110]
输出:6
解释:从上往下数,叠罗汉最多能叠 6 层:(56,90), (60,95), (65,100), (68,110), (70,150), (75,190)

提示:

height.length == weight.length <= 10000

解题思路

思路1:最直观的想法是,动态规划。首先将height和weight数组转换为[h,w]格式,然后使用数组排序将其按照身高升序体重降序(身高相同则体重降序)方式排序,这样就将二维最长递增子序列问题转换为体重维度的一维最长递增子序列问题。为什么体重降序而不是升序呢?假设身高[1、2、2、4、5],如果体重[4、1、2、5、7],那么选中[1、2、5、7]显然不符合要求,反之体重[4、2、1、5、7],那么选中[1、5、7]是符合要求的。即身高相同时体重降序来避免选择身高相同的。(动态规划超时)

//按照身高升序体重降序[1,2,2,3,4] / [1,4,2,4,5]
static bool cmp(const vector<int>& a,const vector<int>& b)
{
  return a[0]==b[0]?a[1]>b[1]:a[0]<b[0];
}
int bestSeqAtIndex(vector<int>& height, vector<int>& weight) 
{
  vector<vector<int>> hw;
  //建立[h,w]数组
  for(int i=0;i<height.size();i++)
  {
     hw.push_back({height[i],weight[i]});
  }
  sort(hw.begin(),hw.end(),cmp);
  //dp[i]表示以某个人为顶层时最大叠罗汉高度
  vector<int> dp(height.size(),1);
  int maxc=INT_MIN;
  for(int i=0;i<hw.size();i++)
  {
    for(int j=0;j<i;j++)
    {
        if(hw[i][0]>hw[j][0]&&hw[i][1]>hw[j][1])
           dp[i]=max(dp[i],dp[j]+1);
        maxc=max(maxc,dp[i]);
    }
  }
  return maxc;
}

总结:数组排序方法sort的第三个参数cmp,注意其必须是static,并且参数为const。

优化:上述动态规划方法是超时的,那么就需要优化算法啦。dp[i]表示长度为i+1的最长递增子序列的尾部的最小值,依次遍历[h,w]数组,找到在数组中插入w仍保持有序的最小位置(找第一个大于等于x),如果没找到则插入末尾,反之找到了则将对应位置元素替换为w,最后数组长度即为最长递增子序列。比如[4、2、1、5、7]:首先dp为空则插入4,即dp变为[4];接着2,找到第一个大于等于2的最小位置4,则dp变为[2];接着1,找到第一个大于等于1的最小位置2,则dp变为[1];接着5,没找到第一个大于等于5的最小位置,则直接插入5,则dp变为[1、5];接着7,没找到第一个大于等于7的最小位置,则直接插入7,则dp变为[1、5、7],即可完成。

//按照身高升序体重降序[1,2,2,3,4] / [1,4,2,4,5]
static bool cmp(const vector<int>& a,const vector<int>& b)
{
   return a[0]==b[0]?a[1]>b[1]:a[0]<b[0];
}
int bestSeqAtIndex(vector<int>& height, vector<int>& weight) 
{
   vector<vector<int>> hw;
   //建立[h,w]数组
   for(int i=0;i<height.size();i++)
   {
     hw.push_back({height[i],weight[i]});
   }
   sort(hw.begin(),hw.end(),cmp);
   //dp[i]表示长度为i+1的最长递增子序列的尾部的最小值
   vector<int> dp;
   for(auto vv:hw)
   {
     auto h=vv[0];
     auto w=vv[1];
     //返回在数组中插入w仍保持有序的最小位置(找第一个大于等于x)
     auto pos=lower_bound(dp.begin(),dp.end(),w);
     //没找到则插入末尾
     if(pos==dp.end())
       dp.push_back(w);
     //找到则将对应位置元素设为w
     else
       *pos=w;
   }
   //最后数组长度即为最长递增子序列
   return dp.size();
}

总结:注意,数组必须有序才可以使用lower_bound和upper_bound。lower_bound(起始地址,结束地址,要查找的值x) ,返回的是在数组中插入x并且插入后仍保持有序的最小的可以插入的位置;lower_bound(起始地址,结束地址,要查找的值x) -起始地址,返回的是第一次出现大于等于x的下标;upper_bound(起始地址,结束地址,要查找的值x) ,返回的是在数组中插入x并且插入后仍保持有序的最大的可以插入的位置;upper_bound(起始地址,结束地址,要查找的值x) -起始地址,返回的是第一个大于x的下标。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值