1107: Too Simple
Time Limit: 2 Sec Memory Limit: 128 MB[ Submit][ Status][ Web Board]
Description
一棵n个点的树, 树上每个点有两个权值x ,y。 x, y都是整数。
现在要从树上选出尽可能多的点重新建边组成一棵新树, (仅仅一棵新树)。
新树要满足,树上任意两个点的权值(xi, yi), (xj, yj) 都满足xi < xj且yi < yj 或者 满足xi > xj且yi > yj.输出新树最多能由多少个点组成。
Input
多组数据,处理到EOF 不超过10组
第一行整数n , 2 <= n <= 100000
接下来两行 ,每行n个整数,
第一行表示n个点的X权值, x1, x2, x3....xn, 空格隔开。
第二行表示n个点的Y权值, y1, y2, y3....yn,空格隔开。
0 < x, y < 100000000
Output
输出新树最多能有多少个点组成。
Sample Input
5
1 5 3 2 4
8 6 9 3 4
Sample Output
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;
}