一棵n个点的树, 树上每个点有两个权值x ,y。 x, y都是整数。
现在要从树上选出尽可能多的点重新建边组成一棵新树, (仅仅一棵新树)。
新树要满足,树上任意两个点的权值(xi, yi), (xj, yj) 都满足xi < xj且yi < yj 或者 满足xi > xj且yi > yj.输出新树最多能由多少个点组成。
一棵n个点的树, 树上每个点有两个权值x ,y。 x, y都是整数。
现在要从树上选出尽可能多的点重新建边组成一棵新树, (仅仅一棵新树)。
新树要满足,树上任意两个点的权值(xi, yi), (xj, yj) 都满足xi < xj且yi < yj 或者 满足xi > xj且yi > yj.输出新树最多能由多少个点组成。
多组数据,处理到EOF 不超过10组
第一行整数n , 2 <= n <= 100000
接下来两行 ,每行n个整数,
第一行表示n个点的X权值, x1, x2, x3....xn, 空格隔开。
第二行表示n个点的Y权值, y1, y2, y3....yn,空格隔开。
0 < x, y < 100000000
输出新树最多能有多少个点组成。
5
1 5 3 2 4
8 6 9 3 4
3
突然又想到3个月前还有几道题没有A掉,作为强迫症的我肯定忍不了。
第一反应又是O(n^2)的算法:排序(按x升序,再按y升序排序)后求最长上升子序列,TLE过果断放弃这个方法。
又想到排序后基本上类似于一维的最长上升子序列,便找到O(nlogn)的最长上升子序列算法学了一下。WA了,然后贪心删除x重复的点,狂WA十几发,一直以为是二分条件写搓了...躺在床上不久便想出贪心的反例,只得拖到第二天
早上起来激动地去掉贪心,继续连WA十几发,又以为是二分条件写搓了。
在不停的调试中,发现:这样排序的话,某些情况下会使最长的长度减少。
比如:
3
1 4 4
2 1 3
这组数据,
进行到(4,1)时,由于在(4,1)和(1,2)中,y值1<2,导致在最长上升子序列中,第1个点的最小值从(1,2)变为(4,1),从而使(4,3)无法更新最大长度。
刚开始没想到改排序方法,只是加了另一个数组,x相同的点,按y值降序遍历。
AC后想到可以直接按x升序,再按y降序排序,求最长上升子序列时,只用按下标遍历即可。
#include <cstdio> #include <algorithm> using namespace std; struct Node { int x,y; bool operator < (const Node& a) const { return x<a.x||(x==a.x&&y>a.y);//按x升序,再按y降序排序 } }a[100005]; int mn[100005],n; int LIS() { int ans=0,i,l,r,mid; for(i=1;i<=n;++i) { l=1,r=ans; while(l<=r) {//二分找到最大的y值小于关键值a[i].y的下标 mid=(l+r)>>1; if(a[mn[mid]].y<a[i].y) l=mid+1; else r=mid-1; } if(l>ans)//如果最大的y值都比a[i].y小,更新最长的长度以及其下标 mn[ans=l]=i; else if(a[mn[l]].y>a[i].y) mn[l]=i; } return ans; } int main() { int i; while(scanf("%d",&n)==1) { for(i=1;i<=n;++i) scanf("%d",&a[i].x); for(i=1;i<=n;++i) scanf("%d",&a[i].y); sort(a+1,a+n+1); printf("%d\n",LIS()); } return 0; }