动态规划--LIS(nlogn实现,轮船问题或建桥问题)

轮船问题(ship.pas)

【问题描述】

某国家被一条河划分为南北两部分,在南岸和北岸总共有N对城市,每一城市在对岸都有唯一的友好城市,任何两个城市都没有相同的友好城市。每一对友好城市都希望有一条航线来往,于是他们向政府提出了申请。由于河终年有雾。政府决定允许开通的航线就互不交叉(如果两条航线交叉,将有很大机会撞船)。兴建哪些航线以使在安全条件下有最多航线可以被开通。

【输入格式】

输入文件(ship.in):包括了若干组数据,每组数据格式如下:

    第一行两个由空格分隔的整数x,y,10〈=x〈=6000,10〈=y〈=100。x表示河的长度而y表示宽。第二行是一个整数N(1<=N<=5000),表示分布在河两岸的城市对数。接下来的N行每行有两个由空格分隔的正数C,D(C、D〈=x〉,描述每一对友好城市与河起点的距离,C表示北岸城市的距离而D表示南岸城市的距离。在河的同一边,任何两个城市的位置都是不同的。

【输出格式】

输出文件(ship.out):要在连续的若干行里给出每一组数据在安全条件下能够开通的最大航线数目。

【输入输出样例】

Ship.in

30  4

5

 5

 4

 2

 3

 1

Ship.out

3



维护一个一位数组d,并且这个数组是动态扩展的,初始大小为1,d[i]表示最长上升自序列长度是i的所有子串中末尾最小的数,根据这个数字,我们可以知道只要当前考察这个数比d[i]大,那么当前这个数一定能通过d[i]构成一个长度为i+1的上升子序列,当然我们希望在d数组中找到一个尽量靠后的数字,这样我们得到的上升子串的长度最长,查找的时候使用二分搜索,这样时间复杂度下降了
#include <iostream>  
using namespace std;  
  
//D[k] = min{A[i]} (F[i] = k)  
  
int d[500001],a[500001];  
  
int Binary_Search(int ai,int len)  
{  
    int _left=1,_right=len,mid;  
    while(_left<_right)  
    {  
        mid=(_left+_right)/2;  
        if(ai > d[mid])  
            _left=mid+1;  
        else  
            _right=mid-1;  
    }  
    return _left;  
}  
  
int LIS_Length(int n)  
{  
    d[1]=a[1];  
    int len=1;  
    for(int i=2;i<=n;i++)  
    {  
        if(a[i] < d[1])  
            d[1]=a[i];  
        else if(a[i] > d[len])  
        {  
            len++;  
            d[len]=a[i];  
        }  
        else  
        {  
            int j=Binary_Search(a[i],len);  
            if(a[i] > d[j])  //如果d[j]<a[i],那d[j+1]一定大于a[i]
                d[j+1]=a[i];  
            else  
                d[j]=a[i];  
        }  
    }  
    return len;  
}  
  
int main()  
{  
    int n,x,y,len,num=0;  
    while(cin>>n)  
    {  
        num++;  
        for(int i=1;i<=n;i++)  
        {  
            cin>>x>>y;  
            a[x]=y;  
        }  
        len=LIS_Length(n);  
        cout<<"Case "<<num<<":"<<endl;  
        cout<<"My king, at most "<<len<<" road";  
        if(len > 1)  
            cout<<'s';  
        cout<<" can be built."<<endl;  
        cout<<endl;  
    }  
    return 0;  
}  


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值