题目描述
有一条横贯东西的大河,河有笔直的南北两岸,岸上各有位置各不相同的N个城市。北岸的每个城市有且仅有一个友好城市在南岸,而且不同城市的友好城市不相同。每对友好城市都向政府申请在河上开辟一条直线航道连接两个城市,但是由于河上雾太大,政府决定避免任意两条航道交叉,以避免事故。编程帮助政府做出一些批准和拒绝申请的决定,使得在保证任意两条航道不相交的情况下,被批准的申请尽量多。
输入格式
第1行,一个整数N,表示城市数。
第2行到第n+1行,每行两个整数,中间用一个空格隔开,分别表示南岸和北岸的一对友好城市的坐标。
输出格式
仅一行,输出一个整数,表示政府所能批准的最多申请数。
输入输出样例
输入 #1
7
22 4
2 6
10 3
15 12
9 8
17 17
4 2
输出 #1
4
这道题看上去可以说一点思路都没有(反正蒟蒻是这样的)
可以看到他的输入是无序的,我们先按照他们的北岸的编号来排个序。然后来画个小图:
我们用这些数表示他的北岸和南岸
假设是这样的:
北岸1-南岸3
北岸2-南岸5
北岸3-南岸1
北岸4-南岸2
北岸5-南岸4
这个图有很多的交叉,所以我们不得不去舍去一些线。可以发现,把北岸1-南岸3和北岸2-南岸5舍去是最优解,这样能保留下3条边。
我们抽象一下,把上面的图的北岸排序后,南岸其实是这样的:
3 5 1 2 4
刚才做的,是把3和5舍去了,剩下的是1 2 4,也就构成了一个叫做“最长不下降子序列”
好的,那么问题就抽象成了:在北岸有序的情况下,南岸的最长不下降子序列
求最长不下降子序列,首先想到的是O(n^2)的做法,可是n<=200000,n^2超时了,只有50分
50分做法:
# include <iostream>
# include <cstdio>
# include <algorithm>
# include <cstring>
using namespace std;
# define int long long
int n,maxn=-1;
int f[200005];
struct node{
int nor,sou;
}map[200005];
bool cmp(node x,node y){
return x.nor<y.nor;
}
signed main(){
scanf("%lld",&n);
for (int i=1;i<=n;i++){
scanf("%lld%lld",&map[i].nor,&map[i].sou);
f[i]=1;
}
sort(map+1,map+1+n,cmp);
f[0]=1;
for (int i=1;i<=n;i++){
for (int j=1;j<i;j++){
if (map[j].sou<=map[i].sou){
f[i]=max(f[i],f[j]+1);
}
maxn=max(maxn,f[i]);
}
}
printf("%lld",maxn);
return 0;
}
(没啥可说的代码模板)
那么我们现在就要去优化,把j循环去了(最优)
还是画个小图,模拟下求最长不下降子序列的过程
这样一个序列,并创建一个数组,存储最长不下降子序列
首先,100加入子序列
再去考虑96,96如果加入子序列,100就会被踢掉,但96更小,所以保留96踢100
考虑334,他进入序列没问题
考虑25,他进入序列,就会把两个数都踢了,不划算,不要了
考虑162,他进来会把334踢了,可以
考虑131,他进来会把162踢了,没事
所以最后就是96 131
他这道题只问你最后的长度,具体的数不考虑,所以这样没问题
那么最后就用代码模拟这个过程就行
# include <iostream>
# include <cstdio>
# include <algorithm>
# include <cstring>
using namespace std;
# define int long long
int n,maxn;
int f[200005];
struct node{
int nor,sou;
}map[200005];
bool cmp(node x,node y){
return x.nor<y.nor;
}
int south[200005];
int left,right;
signed main(){
scanf("%lld",&n);
for (int i=1;i<=n;i++){
scanf("%lld%lld",&map[i].nor,&map[i].sou);
}
sort(map+1,map+1+n,cmp);
south[++maxn]=map[1].sou;
for (int i=2;i<=n;i++){
/*for (int j=1;j<i;j++){
if (map[j].sou<=map[i].sou){
f[i]=max(f[i],f[j]+1);
}
maxn=max(maxn,f[i]);
}*/
int ans=upper_bound(south+1,south+1+maxn,map[i].sou)-south;//寻找第一个>目标值的数
south[ans]=map[i].sou;
if (ans>maxn){
maxn++;
}
}
printf("%lld",maxn);
return 0;
}