求最长升序子序列O(nlgn)的算法---HDOJ 1025

求解最长升/降序子序列是动规的经典问题,朴素动规的时间复杂度为O(n^2),
状态的转移方程为dp[i]=max{dp[j]}+1,其中j<i且arr[j]<arr[i];
现在我们对上面的转移方程再做一个限定,当有多个j满足条件时,
即有多条转移路径的时候,我们规定选择从元素值最小的那个状态转移,
并且用一个数组min[k]来保存dp值为k的最小元素,这样min数组的值
是单调递增的,所以在计算dp[i]时就可以用二分代替线性扫描,从而

将复杂度降低至lgn,这样,总的复杂度变成了nlgn.

#include<cstdio>
#include<algorithm>
#define N 500500

using namespace std;

struct road
{
    int x,y;
    bool operator < (const road &a) const
    {
         return x<a.x;
    }
}arr[N];

int n,dp[N],marr[N],len,inf;

int bs(int *a,int s,int e,int k)
{
    int mid=(s+e)>>1;
    if(a[mid]<=k && mid>=e) return mid;
    if(a[mid]<=k && k<a[mid+1]) return mid;
    if(a[mid]>k) return bs(a,s,mid-1,k);
    else return bs(a,mid+1,e,k);
}

void dpro(void)
{
     for(int i=1;i<=n;i++) if(arr[i].y<inf) inf=arr[i].y;
     inf--;
     marr[0]=inf;
     len=0;
     for(int i=1;i<=n;i++) dp[i]=1;
     for(int i=1;i<=n;i++) {
          int idx = bs(marr,0,len,arr[i].y);
          marr[idx+1]=arr[i].y;
          if(idx==len) len++;
          dp[i]=idx+1;
     }
}

int main()
{
    int cas=1;
    while(scanf("%d",&n)!=EOF) {
        for(int i=1;i<=n;i++) scanf("%d%d",&arr[i].x,&arr[i].y);
        sort(arr+1,arr+n+1);
        dpro();
        int max=0;
        for(int i=0;i<n;i++) if(max<dp[i]) max=dp[i];
        printf("Case %d:\n",cas++);
        printf("My king, at most %d road",len);
        if(len>1) printf("s");
        printf(" can be built.\n\n");
    }
    return 0;
}



  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值