北大1631题

 题目连接:http://acm.pku.edu.cn/JudgeOnline/problem?id=1631

写了三个版本,第一个版本到现在也没搞清楚哪里有问题

 

版本一:

1,这个问题就是要求线路的最大不相交子集中元素的个数,opt[i][j]代表左方前i条线在右方j个接线柱范围内的最优值

2,最优子结构是:opt[i][j] = opt[i-1][j](end[i]>j时)或者max(opt[i-1][j],opt[i-1][end[i]-1]+1)(end[i]<=j时)

3,边界条件是:opt[1][j] = 0(j<end[1]时)或者1(j>=end[1]时)

#include <iostream>
using namespace std;

#define MAX 5000

int end[MAX],size;
int t,c,p,i,j;
unsigned short opt[2][MAX];
int main()
{
 freopen("in.txt","r",stdin);

 cin >> t;
 while(t--)
 {
  cin >> size;
  for(i = 1;i <= size;++i)
   cin >> end[i];

  for(i = 0;i <= size;++i)
  {
   if(i < end[1])
    opt[0][i] = 0;
   else
    opt[0][i] = 1;
  }

  for(i = 2;i <= size;++i)
  {
   for(j = 0;j <= size;++j)
   {
    c = (i+1) % 2;
    p = (c == 0 ? 1 : 0);
    if(j < end[i])
     opt[c][j] = opt[p][j];
    else
     opt[c][j] = opt[p][j] > opt[p][end[i]-1] + 1?
        opt[p][j] : opt[p][end[i]-1] + 1;
   }
  }
  cout << opt[(size+1)%2][size] << endl;
 }
 return 0;
}

 

版本二:

1,nlogn版本的单调上升子序列算法

2,opt[i]代表以i元素结尾的最长上升子序列的长度,mm[i]代表所有长度为i的上升子序列中最小的末尾元素。

3,假设当前考察元素为end[i],它该加到谁的后面呢?若end[i]比前面的所有元素都小那么它应该自立门户,成为长度为1的子序列,此时应该看它是否比mm[1]中的元素小,若更小就更新mm[1]中的值;若end[i]比前面的所有元素都大那么它可以加到已知的最长上升序列的后面成为更长的子序列,此时mm的长度加1,存上end[i]的值;若end[i]不是这两种极端情况,该加到谁的后面?找到一个一存在的最长的上升子序列还要满足该子序列最后一个元素小于end[i],这些信息通过二分查找mm数组即可实现,方法就是让lf始终指向大于等于end[i]的元素,这样在循环跳出的时候lf指向了mm中第一个不小于end[i]的元素,换句话说lf左边第一个元素就是小于end[i]的元素,end[i]可以加到lf左边第一个元素的后面形成长度为lf的上升子序列,所以此时lf就是以end[i]结尾的最长上升子序列的长度,end[i]总是不大于lf当前所指向的元素,所以可以用end[i]更新lf处的元素。

#include <iostream>
using namespace std;

#define MAX 40000

int end[MAX],size;
int opt[MAX],mm[MAX];
int i,j,t,lf,rt,md,len;
int main()
{
 freopen("in.txt","r",stdin);

 cin >> t;
 while(t--)
 {
  cin >> size;
  for(i = 0;i < size;++i)
   cin >> end[i];

  len = 1;
  opt[0] = 1;
  mm[len] = end[0];

  for(i = 1;i < size;++i)
  {
   lf = 1;
   rt = len;
   while(lf <= rt)
   {
    md = (lf+rt) / 2;
    if(mm[md] < end[i])
     lf = md + 1;
    else
     rt = md - 1;
   }

   if(lf > len)
    len = lf;

   opt[i] = lf;
   mm[lf] = end[i];
  }

  cout << len << endl;
 }
 return 0;
}

 

版本三:

这里主要是考虑到了mm其实包含了长度信息,就不用再开opt数组了,节省了点内存!

#include <iostream>
using namespace std;

#define MAX 100

int end[MAX],size;
int opt[MAX],len;
int i,j,lf,md,rt,t;

int main()
{
 freopen("in.txt","r",stdin);

 cin >> t;
 while(t--)
 {
  cin >> size;
  for(i = 0;i < size;++i)
   cin >> end[i];

  opt[1] = end[0];
  len = 1;
  
  for(i = 1;i < size;++i)
  {
   lf = 1;
   rt = len;
   while(lf <= rt)
   {
    md = (lf+rt) / 2;
    if(opt[md] < end[i])
     lf = md + 1;
    else
     rt = md - 1;
   }

   //lf指向第一个大于或者等于end[i]的位置
   if(len < lf)
    len = lf;

   opt[lf] = end[i];
  }

  cout << len << endl;
 }
 return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值