链接:
题意:
一个信封有长宽,如果一个信封的长宽均严格大于另一个信封,那么大的这个信封可以装下小的这个信封
求最多能套娃几个信封
解:
类似普通的最长上升子序列,但是信封有两个数据,第一时间想到的是优先队列排序,但是我们发现这两个数据并没有优先级,也不能通过加减乘除之类的操作制造一个新的关键数据
我们看一下正常最长上升子序列的解法动态规划的LIS算法,它的DP[i-1]
(下标初始为0,使用后置++的方法赋值和保存长度)表示长度i
的上升子序列最后一位的最小值。
主要过程我将他分为两步:更新&扩展,更新是指我的数值并不足以扩展出更长的上升子序列,但是我可以使某个长度的上升子序列最后一位更小(结尾越小理论越长);扩展则是指我的数值可以完成扩展,那我就增加DP计数并保存这个数值。
那假如我们用w升序,h在此基础上升序,那么对于w来说,后进的永远大于等于先进的,这是部分符合的,所以如果我们遇到w5h15,w10h10,w15h20,完全可以用w10h10更新w5h15(w10也能满足w15,优先选择h最小的);但是如果存在同w下h的升序,例如w5h15,w10h10,w10h20,我们就能发现更新完以后,w10h20并不能接在w10h10后面,反而应该接在w5h15后面。
我们分析这个错误就可以发现,如果w升序排序,我们就可以用大的w小的h更新原先小的w大的h,如果同w情况下h升序排序,更新会先进行,先更新的后果/错误就是如果被更新的是目前求出来的最长子序列最后一位的最小值,那么本来能扩展长度的数据就会因为这个更新后的w和自身相同无法完成扩展,或者更新后的h小于原先的h进行错误扩展(看你怎么判断扩展的=-=)(当然也会导致其他更新的错误)
先更新后扩展不行,能不能先扩展后更新,实际上这就是答案:让w升序的情况下h降序,这样同w的情况下大的h就会先进行处理。
在处理一组新的w之前,我们已知数组里的old-w都小于这个new-w,那么如果new-w的h大于最后一位,就可以进行扩展,然后再遍历new-w中h降序的其他数据,来更新DP数组;我们能确保更新并不会导致已经求出的最长子序列长度变短,同时数组里只包含new-w和小于new-w的old-w;在条件w1>=w2&&h1<h2
下,我们可知用w1h1更新w2h2将会是合法的。
实际代码:
#include<bits/stdc++.h>
using namespace std;
struct cmp
{
bool operator() (const pair<int,int> &A,const pair<int,int> &B)
{
if(A.first==B.first) return A.second<B.second;
else return A.first>B.first;
}
};
int maxEnvelopes(vector<vector<int>>& envelopes)
{
int lg=envelopes.size(),now=0;
vector<int>dp(lg);
priority_queue<pair<int,int>,vector<pair<int,int>>,cmp>pq;
for(const auto &envelope:envelopes) pq.push({envelope[0],envelope[1]});
while(!pq.empty())
{
pair<int,int>temp=pq.top();pq.pop();
if(now==0) dp[now++]=temp.second;
else
{
if(temp.second>dp[now-1]) dp[now++]=temp.second;
else
{
auto upper=lower_bound(dp.begin(),dp.begin()+now,temp.second);
if(upper==dp.begin()+now||upper==dp.end()) continue;//没找到
*upper=min(*upper,temp.second);
}
}
}
return now;
}
int main()
{
vector<vector<int>> envelopes;
int n;cin>>n;
for(int i=0;i<n;i++)
{
int a,b;cin>>a>>b;
envelopes.push_back({a,b});
}
int ans=maxEnvelopes(envelopes);
cout<<ans<<endl;
return 0;
}
限制:
1 <= envelopes.length <= 105
envelopes[i].length == 2
1 <= wi, hi <= 105