基准时间限制:1 秒 空间限制:131072 KB 分值: 0 难度:基础题
收藏
关注
给出长度为N的数组,找出这个数组的最长递增子序列。(递增子序列是指,子序列的元素是递增的)
例如:5 1 6 8 2 4 5 10,最长递增子序列是1 2 4 5 10。
Input
第1行:1个数N,N为序列的长度(2 <= N <= 50000)
第2 - N + 1行:每行1个数,对应序列的元素(-10^9 <= S[i] <= 10^9)
Output
输出最长递增子序列的长度。
Input示例
8
5
1
6
8
2
4
5
10
Output示例
5
一道51上难度为0的题,结果弄得很麻烦。。。看来还是太菜了。
好了,看下题吧,求最长递增子序列,emmmmm.......基础字符串题型,最一开始用的动态规划。
↓↓↓
#include <stdio.h>
#include <string.h>
typedef long long ElemType;
int dp[50010];
long long str[50010];
int list( ElemType *c , int len ){
memset( dp,0,sizeof(dp) );
int max = 0;
for( int i=1 ; i<len ; i++ ){
for( int j=0 ; j<i ; j++ ){
if( c[i]>c[j] && dp[i]<dp[j]+1 )
dp[i] = dp[j] + 1;
}
if( max<dp[i] )
max = dp[i];
}
return ++max;
}
int main(){
int n;
scanf( "%d",&n );
for( int i=0 ; i<n ; i++ ){
scanf( "%lld",&str[i] );
}
printf( "%d\n",list( str,n ) );
}
完美的超时了最后五组数据。。。略有一丝尴尬,,,可能是刚刚来还是不太熟悉51吧,后来借助博客上的ACM大神提点:转载大神博客 ,让我明白了二分算法的重要性。。。于是,自认为看懂了之后照这样子写了一个。。。
↓↓↓
#include <stdio.h>
#include <string.h>
#define MAXN 50010
typedef long long ElemType ;
int n; //数组num的实际长度
long long dp[MAXN]; //记录每一长度的最小末尾元素
long long num[MAXN] ; //初始序列
//二分查找当前元素value应该插入的位置
int bin_serach( ElemType *c , int len , ElemType value ){
int left = 0 ;
int right = len-1 ;
int mid;
//二分,左右游标相等时停止
while( left<=right ){
mid = ( left+right )/2;
//mid位置元素值大于value,目标位置在左半部分
if( c[mid]>value )
right = mid - 1 ;
//mid位置元素值大于value,目标位置在右半部分
else if( c[mid]<value )
left = mid + 1 ;
//mid位置元素值与value相等 ,已找到,返回mid
else
return mid;
}
//未找到该元素,此时返回比value大的最小元素的位置,更新元素值
return left;
}
//求最长递增子序列的函数
int list( ElemType *c , int n ){
memset( dp,0,sizeof(dp) );
int len=0;
for( int i=0 ; i<n ; i++ ){
//如果num[i]比当前最长长度末尾元素还大,长度+1
if( num[i]>dp[len] )
dp[++len] = num[i];
else{
//取得元素num[i]应该插入的位置
int pos = bin_serach( dp,len,num[i] );
dp[pos] = num[i];
}
}
return len;
}
int main(){
scanf( "%d",&n );
for( int i=0 ; i<n ; i++ )
scanf( "%lld",&num[i] );
printf( "%d\n",list( dp,n ) ) ;
/* for( int i=0 ; i<=list( dp,n ) ; i++ ){
printf( "%lld ",dp[i] );
}
printf( "\n" );
*/
}
嗯,写的不错,测试了一下,完美通过hhhh,然后测试了第一组样例答案错误????蜜汁尴尬。。。然后用一个for循环一步一步追踪了一下dp数组,发现dp数组里有负数???好吧,我的错。。于是我找来了无穷小,然后变成了这样
↓↓↓
#include <stdio.h>
#include <string.h>
#define MAXN 50010
#define MINN 0xc0c0c0c0
typedef long long ElemType ;
int n; //数组num的实际长度
long long dp[MAXN]; //记录每一长度的最小末尾元素
long long num[MAXN] ; //初始序列
//二分查找当前元素value应该插入的位置
int bin_serach( ElemType *c , int len , ElemType value ){
int left = 0 ;
int right = len-1 ;
int mid;
//二分,左右游标错过时停止
while( left<=right ){
mid = ( left+right )/2;
//mid位置元素值大于value,目标位置在左半部分
if( c[mid]>value )
right = mid - 1 ;
//mid位置元素值大于value,目标位置在右半部分
else if( c[mid]<value )
left = mid + 1 ;
//mid位置元素值与value相等 ,已找到,返回mid
else
return mid;
}
//未找到该元素,此时返回比value大的最小元素的位置,更新元素值
return left;
}
//求最长递增子序列的函数
int list( ElemType *c , int n ){
memset( dp,0xc0,sizeof(dp) );
int len=0;
for( int i=0 ; i<n ; i++ ){
//如果num[i]比当前最长长度末尾元素还大,长度+1
if( num[i]>dp[len] )
dp[++len] = num[i];
else{
//取得元素num[i]应该插入的位置
int pos = bin_serach( dp,len,num[i] );
dp[pos] = num[i];
}
}
return len;
}
int main(){
scanf( "%d",&n );
for( int i=0 ; i<n ; i++ )
scanf( "%lld",&num[i] );
printf( "%d\n",list( dp,n ) ) ;
/* for( int i=0 ; i<=list( dp,n ) ; i++ ){
printf( "%lld ",dp[i] );
}
printf( "\n" );
*/
}
嗯,没错,这次完美通过,下面来解释一下算法的思想,,,那num[8]={ 5,1,6,8,2,4,5,10 }来说吧。
首先len=0。list函数开始遍历num数组:
i=0时:此时dp输出都为无穷小哦,理所当然的dp[0]=5,len=1了哦(dp[]={ 5 })
i=1时:此时num[1]=1比5小,这个dp呢我们是来存储每一个长度的最小末尾的(这一句不理解没关系,接着看就理解了),此时呢len=1,而1又比5小,所以就将dp[0]=5更新为dp[0]=1(因为递增嘛,长度相同的时候当然是越小越好咯)(dp[]={ 1 })
i=2时:此时num[2]=6比1大,所以dp[1]=6,len+1,len=2了啊(dp[]={ 1,6 })
i=3时:此时num[3]=8比6大,所以dp[2]=8,len+1,len=3了哦(dp[]={ 1,6,8 })
i=4时:此时num[4]=2,而看看dp,比2大的最小的是6,于是就把len=2时的最小末尾更新为2(dp[]={ 1,2,8 })
i=5时:此时num[5]=4,看dp,把len=3时的最小末尾更新为4(dp[]={ 1,2,4 })
i=6时:此时num[6]=5,比dp最大的数还大,dp[4]=5,len+1,len=4(dp[]={ 1,2,4,5 })
i=7时:此时num[7]=10,最大的,dp[5]=10,len+1,len=5(dp[]={ 1,2,4,5,10 })
此时1,2,4,5,10就是num数组的最长递增子序列了