P2782 友好城市

P2782 友好城市

分析

分析下,航道不交叉的条件。

如果有一条已经被批准的航道,南北坐标为 N 1 , S 1 N1,S1 N1,S1

则第二条 N 2 < N 1 , S 2 > S 1 N2 \lt N1, S2 \gt S1 N2<N1,S2>S1(或恰好相反)的航道将不能被批准,因为他们发生了交叉。

参照此图

在这里插入图片描述
在这里插入图片描述

​ 因此,要满足题目中的条件,需要保证南岸序列和北岸序列 单调上升(题目中交代了坐标不会重合)。

​ 我们按北岸坐标大小对城市进行排序,来保证北岸序列单调上升;

​ 然后,南岸序列的 最长上升子序列 长度就能够批准的航线条数。

没有学过的叉出去就行了。

简单回顾,包含 a [ i ] a[i] a[i] 为结尾的最长上升子序列长度。

  • O ( n 2 ) O(n^2) O(n2) 的做法:

    处理 a [ i ] a[i] a[i],判断 a [ j ] < a [ i ] a[j] < a[i] a[j]<a[i] 其中 $ 1 \le j \lt i$,尝试加入到以 a [ j ] a[j] a[j] 为结尾的序列上是否可以更长,如果可以则更新。

  • O ( n l o g n ) O(nlogn) O(nlogn) 的做法:

    ​ 使用辅助数组 b [ l e n ] b[len] b[len] 来存放目前处理的最长上升子序列元素,保持 单调性。如果 a [ i ] > b [ l e n ] a[i] \gt b[len] a[i]>b[len],直接入队。如果 a [ i ] ≤ b [ l e n ] a[i] \le b[len] a[i]b[len],在 b[] 中查找第一个大于等于 a [ i ] a[i] a[i] 的位置进行替换。

本题数据范围: n = 2 e 5 , O ( n 2 ) = 4 e 10 n = 2e5,O(n^2) = 4e10 n=2e5,O(n2)=4e10,超时,所以需要用二分进行优化!

代码

#include<bits/stdc++.h>
using namespace std;

const int N = 2e5 + 10;
struct node {
	int north, south; //北方、南方
} p[N];

bool cmp( node &a, node &b ) {
	return a.north < b.north;
}

int n;
int len, b[N]; // 存放当前子序列的数组

int main() {
	cin >> n;
	for (int i = 1; i <= n; i++) {
		cin >> p[i].south >> p[i].north;
	}

	sort( p + 1, p + 1 + n, cmp );

	b[++len] = p[1].south; // 第一个城市之前没有可比,直接放置

	for (int i = 2; i <= n; i++) {
		if ( p[i].south > b[len] ) {
			// 大于末尾,直接放置
			b[++len] = p[i].south;
		}
		else {
			// 二分查找,第一个大于等于目标元素的位置进行替换
			int pos = lower_bound( b + 1, b + 1 + len, p[i].south ) - b;
			b[pos] = p[i].south; // 替换
		}
	}
	cout << len << endl;
	return 0;
}
  • 29
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值