有个马戏团正在设计叠罗汉的表演节目,一个人要站在另一人的肩膀上。出于实际和美观的考虑,在上面的人要比下面的人矮一点且轻一点。已知马戏团每个人的身高和体重,请编写代码计算叠罗汉最多能叠几个人。
思路:首先按照身高从低到高排序,然后找到对应体重的最长递增子序列。
注意身高和体重需要严格递增,不可以相等。
解法一: 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();
}
};