最长上升子序列(HDU 1423,PKU 1887,ZJU1986)(c++) 可输出序列

最长上升子序列(HDU 1423, PKU 1887,ZJU1986)

假如序列为3 2 8 6 7 4 5 7 3,求最大上升子序列:

传说中O(n^2)算法,一般人能想到(DP)

for (i=0;i<n;i++)

    {

        max=0;

         for (j=0;j<i;j++)

             if ((a[i]>=a[j]) &&(max<d[j]))max=d[j];

         d[i]=max+1;

         if (fmax<d[i]) fmax=d[i];

    }

    printf("%d",fmax);

传说中O(n*logn)算法,节约时间空间……

(二分查找和贪心)

数组实现:

const int N = 40001;

int a[N], D[N], n;

void DP ( )

{

        scanf("%d", &n);

        int i, len = 0;

        for ( i = 0; i < n; i++ )

        scanf("%d", &a[i]);

        memset(D, 0, sizeof(D));

        int m, l, r;

        for ( i = 0; i < n; i++ )

              if ( a[i] > D[len] )

                      D[++len] = a[i];

              else

              {

                      l = 0, r = len;

                      while ( l < r - 1 )

                      {

                              m = (l + r) / 2;

                              if ( a[i] >D[m] )

                                      l = m;

                              else

                                      r = m;

                        }

                      if ( a[i] < D[l] )

                              D[l] = a[i];

                      else

                              D[r] = a[i];

              }

              printf("%d/n", len);

}

(需要注意的是,D[]在算法结束后记录的并不是一个符合题意的最长上升子序列,用堆栈实现的原理相同,但用堆来优化的话还没搞懂最后怎样把序列输出来)

 

 

 

那如果要输出序列呢?

是不是单单用数组D呢?

不是的,以3 2 8 6 7 4 5 7 3为例,D数组是2 3 5 7,这显然不对,因为在计算中我们只是取单组最大的值来保存长度,所以,要输出序列就要在中间保存一下:

#define MAX 40005

inta[MAX],b[MAX],p,n,D[MAX],Left,Right,mid,blen,num;

void DP()

{

         blen = 0;

    int i;

         scanf("%d",&p);

         for (i = 1;i <= p;i++)

                   cin>>numbers[i];

         int ans = b[1];

         for (i = 1;i <= p;i++)

         {

                   num =a[i];

                   Left = 1;

                   Right = blen;

                   while (Right >= Left)

                   {

                            mid = (Left + Right)/2;

                            if (D[mid] >= num)

                                     Right = mid - 1;

                            else

                                     Left = mid + 1;

                   }

                   b[i] = Left;

                   D[Left] = num;

                   if (Left > blen)   //check if the blen should plus one itself

                            blen = Left;

                   if (ans < b[i])

                            ans = b[i];

         }

         printf("%d/n",ans);

}

这里的的D[]就保存了每个结尾的最大子序列长度

b   :    01 12 2 3 2342

a   :    03 2 8 6 7 4 5 7 3

用b从后往前就可以看到最大上升子序列了

 

(欢迎大家指正  资料参考部分模板 )

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值