LIS系列问题(hdu 4521、poj 3903)

小明系列问题——小明序列

Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others)
Total Submission(s): 1848    Accepted Submission(s): 564


Problem Description
  大家都知道小明最喜欢研究跟序列有关的问题了,可是也就因为这样,小明几乎已经玩遍各种序列问题了。可怜的小明苦苦地在各大网站上寻找着新的序列问题,可是找来找去都是自己早已研究过的序列。小明想既然找不到,那就自己来发明一个新的序列问题吧!小明想啊想,终于想出了一个新的序列问题,他欣喜若狂,因为是自己想出来的,于是将其新序列问题命名为“小明序列”。

  提起小明序列,他给出的定义是这样的:
  ①首先定义S为一个有序序列,S={ A1 , A2 , A3 , ... , An },n为元素个数 ;
  ②然后定义Sub为S中取出的一个子序列,Sub={ Ai1 , Ai2 , Ai3 , ... , Aim },m为元素个数 ;
  ③其中Sub满足 Ai1 < Ai2 < Ai3 < ... < Aij-1 < Aij < Aij+1 < ... < Aim ;
  ④同时Sub满足对于任意相连的两个Aij-1与Aij都有 ij - ij-1 > d (1 < j <= m, d为给定的整数);
  ⑤显然满足这样的Sub子序列会有许许多多,而在取出的这些子序列Sub中,元素个数最多的称为“小明序列”(即m最大的一个Sub子序列)。
  例如:序列S={2,1,3,4} ,其中d=1;
  可得“小明序列”的m=2。即Sub={2,3}或者{2,4}或者{1,4}都是“小明序列”。

  当小明发明了“小明序列”那一刻,情绪非常激动,以至于头脑凌乱,于是他想请你来帮他算算在给定的S序列以及整数d的情况下,“小明序列”中的元素需要多少个呢?
 

Input
  输入数据多组,处理到文件结束;
  输入的第一行为两个正整数 n 和 d;(1<=n<=10^5 , 0<=d<=10^5)
  输入的第二行为n个整数A1 , A2 , A3 , ... , An,表示S序列的n个元素。(0<=Ai<=10^5)
 

Output
  请对每组数据输出“小明序列”中的元素需要多少个,每组测试数据输出一行。
 

Sample Input
  
  
2 0 1 2 5 1 3 4 5 1 2 5 2 3 4 5 1 2
 

Sample Output
  
  
2 2 1
 


一个求最长上升子序列变形的问题,限制条件要求相邻两项的下标差值必须大于d。

c[]数组存储当前可用的值。


#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<algorithm>
using namespace std;
#define N 100005
const int inf=0x1f1f1f1f;
int a[N],c[N],dp[N];
int n,d;
int findd(int x)
{
    int l,r,mid;
    l=0;
    r=n-1;
    while(l<=r)
    {
        mid=(l+r)>>1;
        if(c[mid]<x)
            l=mid+1;
        else
            r=mid-1;
    }
    return l;
}
int solve()
{
    int i,j,ans=0;
    for(i=0;i<n;i++)
    {
        dp[i]=findd(a[i]);
        ans=max(ans,dp[i]);
        j=i-d;
        if(j>=0&&c[dp[j]]>a[j])
            c[dp[j]]=a[j];
    }
    return ans;
}
int main()
{
    while(scanf("%d%d",&n,&d)!=-1)
    {
        for(int i=0;i<n;i++)
        {
            scanf("%d",&a[i]);
            c[i]=inf;
        }
        printf("%d\n",solve()+1);
    }
    return 0;
}



Stock Exchange
Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 4405 Accepted: 1555

Description

The world financial crisis is quite a subject. Some people are more relaxed while others are quite anxious. John is one of them. He is very concerned about the evolution of the stock exchange. He follows stock prices every day looking for rising trends. Given a sequence of numbers p1, p2,...,pn representing stock prices, a rising trend is a subsequence pi1 < pi2 < ... < pik, with i1 < i2 < ... < ik. John’s problem is to find very quickly the longest rising trend.

Input

Each data set in the file stands for a particular set of stock prices. A data set starts with the length L (L ≤ 100000) of the sequence of numbers, followed by the numbers (a number fits a long integer). 
White spaces can occur freely in the input. The input data are correct and terminate with an end of file.

Output

The program prints the length of the longest rising trend. 
For each set of data the program prints the result to the standard output from the beginning of a line.

Sample Input

6 
5 2 1 4 5 3 
3  
1 1 1 
4 
4 3 2 1

Sample Output

3 
1 
1

Hint

There are three data sets. In the first case, the length L of the sequence is 6. The sequence is 5, 2, 1, 4, 5, 3. The result for the data set is the length of the longest rising trend: 3.

需要O(n*(logn))的算法才能过:

即假设数组元素 a[x]<a[y]<a[i],x<y<i,且 dp[x]==dp[y],则我们肯定选a[x]的值构成一个最长LIS,

{a[x],a[i]}优于{a[y],a[i]}.

数组c[]是单调上升的,记录满足长度为i的最长上升子序列最优值。

这样我们利用c[],长度为Len,遇到一个a[i]时先判断a[i]与c[len]的大小,若a[i]>c[len],则把a[i]值接到c[len]后面得到一个更长的序列。否则,在数组c[]里查找当前值所在的位置,并把当前值a[i]替换掉查找到的值x,a[i]<=x;


#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;
#define N 100005
const int inf=0x1f1f1f1f;
int a[N],c[N];
int main()
{
	int i,n,len;
	while(scanf("%d",&n)!=-1)
	{
		for(i=0;i<n;i++)
			scanf("%d",&a[i]);
		c[0]=-inf;
		len=0;
		for(i=0;i<n;i++)
		{
			if(a[i]>c[len])
                c[++len]=a[i];
            else
            {
                int l=0,r=len,mid;
                while(l<=r)
                {
                    mid=(l+r)>>1;
                    if(c[mid]<a[i])
                        l=mid+1;
                    else
                        r=mid-1;
                }
                c[l]=a[i];
            }
		}
		printf("%d\n",len);
	}
	return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值