标签:
现在n(<=20000)个俄罗斯套娃,每个都有宽度wi和高度hi(均小于10000),要求w1<w2并且h1<h2的时候才可以合并,问最少能剩几个。
【LIS】乍一看跟【这题】类似,但是仔细看是有区别的,其实就相当于上一题多次求LIS,每次求完LIS后把得到的序列删去,然后重新求LIS,最后输出求LIS的次数,我一开始这样写,果然就TLE了。还是要另辟蹊径。
首先用贪心思想,先按照wi从大到小排序,wi相等的情况下hi从小到大,然后求最长不下降子序列(注意可以等于)。输出其长度即可。
想想为什么,如果有i和j这两个点满足wi>wj,hi>hj,显然是满足条件的,直接合并即可(所求序列长度不变),但是如果wi>wj,hi<=hj不可合并,那么序列长度就要增加。
如果wi==wj,那么无论hi与hj的关系如何都不可以合并,为了方便求最长不下降序列,可以假设hi<=hj,这样序列长度也会增加。因此wi相等的情况下hi要从小到大排序。
还有一个问题是LIS中二分查找的写法问题,我是卡在了这里,在网上看了看别人的代码后,把lower_bound改成了upper_bound竟然AC了!
lower_bound是返回序列中大于等于key值的第一个数
upper_bound是返回序列中严格大于key值的第一个数
显然最长上升子序列是严格递增的,因此每次更新后都不可以出现两个数相同的情况,因此使用lower_bound(比如1,2,3,4,新输入的数是3,使用lower_bound返回第三个数,即把3改成3,如果使用upper_bound返回第四个数,把4改成3,则会出现两个3,不符合条件)。而最长不下降子序列存在多个数相同的情况,因此使用upper_bound,这里解释同上。
#include<cstdio> #include<cstring> #include<cmath> #include<iostream> #include<algorithm> #include<set> #include<map> #include<stack> #include<vector> #include<queue> #include<string> #include<sstream> #define eps 1e-9 #define ALL(x) x.begin(),x.end() #define INS(x) inserter(x,x.begin()) #define FOR(i,j,k) for(int i=j;i<=k;i++) #define MAXN 20005 #define MAXM 40005 #define INF 0x3fffffff using namespace std; typedef long long LL; int i,j,k,n,m,x,y,T,ans,big,cas,num,len; bool flag; struct node { int s,b,i; }p[MAXN]; int dp[MAXN]; bool cmp(node x,node y) { if (x.s==y.s) return x.b<y.b; return x.s>y.s; } int main() { scanf("%d",&T); while (T--) { scanf("%d",&n); for (i=1;i<=n;i++) { scanf("%d%d",&p[i].s,&p[i].b); p[i].i=i; } sort(p+1,p+1+n,cmp); num=0; for (i=1;i<=n;i++)//求最长不下降子序列 { if (p[i].b>=dp[num])//与最长上升子序列求法不同的是这里改成大于等于 { dp[++num]=p[i].b; }else { k=upper_bound(dp+1,dp+1+num,p[i].b)-dp; //与最长上升子序列求法不同的是这里改成upperbound dp[k]=p[i].b; } } printf("%d\n",num); } return 0; }
SPOJ 3943 - Nested Dolls 最长不下降子序列LIS(二分写法)
标签:
原文地址:http://www.cnblogs.com/zhyfzy/p/4285473.html