【训练题】航线设计(优化求较大数据规模的最长单调子序列)

【问题描述】

  有一个国家被一条河划分为南北两部分,在南岸和北岸总共有N对城镇,每一城镇在对岸都有唯一的友好城镇。任何两个城镇都没有相同的友好城镇。每一对友好城镇都希望有一条航线来往。于是他们向政府提出了申请。由于河终年有雾。政府决定不允许有任两条航线交叉(如果两条航线交叉,将有很大机会撞船)。

  你的任务是写一个程序来帮政府官员决定他们应拨款兴建哪些航线以使得没有出现交叉的航线最多。

【输入格式】

  第一行一个整数N,表示分布在河两岸的城镇对数。接下来的N行每行有两个由空格分隔的正数C,D(C、D<=10^9〉,描述每一对友好城镇沿着河岸与西边境线的距离,C表示北岸城镇的距离而D表示南岸城镇的距离。在河的同一边,任何两个城镇的位置都是不同的。

【输出格式】

  在安全条件下能够开通的最大航线数目。

【输入样例】

7
22 4
2 6
10 3
15 12
9 8
17 17
4 2

【输出样例】

4

【数据范围】

  1<=N<=500000

其实一眼就能看出来要将友好城市对按左岸城市的位置由小到大排序,求右岸城市序列的最长上升子序列。

之前学过时间复杂度为O(n^2)的求最长单调子序列的算法,然而在这道题中是不可能使用的。很明显针对n最大为500000的数据需要时间复杂度为O(n*log2n)甚至更小的算法。

**设状态函数d[i]表示 长度为i的 最长上升子序列的 最后一个数的 最小值**

由于状态函数的定义方法,d一定是有序的,于是从1到n遍历的过程中就可以使用二分查找大于a[i]的第一个元素,将其直接替换为a[i]替换后仍然有序,并且若a[i]为当前最大值,就可以直接塞到d的最后,二分查找函数的返回值不需要任何改动就可以使用。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<cctype>
#include<vector>
#include<set>
#define maxn 500005
#define oo 200000000
using namespace std;
int n,d[maxn]={0};
struct line
{
    int l,r;
}a[maxn];
bool cmp(line a,line b)
{
    return a.l<b.l;
}

void read(int &x)
{
    x=0;
    bool ok=0;
    char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')
    {
        ch=getchar();
    }
    while((ch>='0'&&ch<='9')||ch=='-')
    {
        if(ch=='-') ok=1;
        else x=x*10+ch-'0';
        ch=getchar();
    }
    if(ok) x=-x;
}

void in()
{
    read(n);
    for(int i=1;i<=n;i++)
    {
        read(a[i].l);
        read(a[i].r);
    }
    sort(a+1,a+n+1,cmp);
}

void task()
{
    int ans=0;
    for(int i=1;i<=n;i++)
    {
        int t=upper_bound(d+1,d+ans+1,a[i].r)-d;
        d[t]=a[i].r;
        ans=max(ans,t);
    }
    printf("%d",ans);
}

int main()
{
    //freopen("in1.txt","r",stdin);
    in();
    task();
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_35546348/article/details/52424074