最长子序列LIS
(1).暴力dp
//LIS 基础dp法
//思路:1.dp[i]储存的是以i结尾的最长上升子序列长度
//2.跟前边的aj比较, 如果a[i]>a[j],就a[i]可以加到j序列后边,
//所以dp[i]=dp[j]+1 ,多次取max即可
//ps 如果都不可以加,则dp[i] = 1 ;可以理解为创造新序列
//3.ans多次取max 即:ans=max(ans,dp[i])
#include<iostream>
#include<cstdio>
using namespace std ;
const int maxn = 3e4+100 ;
int dp[maxn] , a[maxn] ;
int main()
{
int t ;
while(~scanf("%d",&t))
{
for(int i = 1 ; i <= t ; i++)
{
scanf("%d",&a[i]) ;
dp[i] = 1 ;
}
int ans = dp[1] ;
for(int i = 1 ; i <= t ; i++)
{
for(int j = 1 ; j < i ; j++)
{
if(a[j] < a[i]) dp[i] = max(dp[i],dp[j]+1) ;
}
ans = max(ans,dp[i]) ;
}
printf("%d\n",ans) ;
}
return 0 ;
}
二分
//二分思路:
//1.a[i]>d[len]即a[i]大于当前最长子序列的最后一个元素,就加在序列之后:d[++len]=a[i]
//2.否则 lower_bound 找到大于等于的元素,替代掉
//即 j = lower_bound(d+1,d+1+len,a[i])-d
//d[j] = a[i]
//ps.因为d[0]=0,所以-d ;如果-(d+1),则会插在前一位 如:5-0=5;5-1=4
//3.ps如果小于的话,就插在第一位,即替换掉第一个数
//所以初始化序列d[0]=0 ;
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std ;
const int maxn = 3e4+5 ;
int d[maxn], a[maxn] ;//d储存该序列,len储存长度
int main()
{
int t ;
while(~scanf("%d",&t))
{
memset(a,0,sizeof(a)) ;
for(int i = 1 ; i <= t ; i++) scanf("%d",&a[i]) ;
int len = 0 ;
d[0] = 0 ;
for(int i = 1 ; i <= t ; i++)
{
if(a[i] > d[len])
{
d[++len] = a[i] ;
//printf("d[%d]=%d\n",len,d[len]) ;
}
else
{
int j = lower_bound(d+1,d+len+1,a[i])-d ;
d[j] = a[i] ;
//printf("d[%d]=%d\n",j,d[j]) ;
}
}
printf("%d\n",len) ;
}
return 0 ;
}
二者比较
//比较dp与二分:
//dp双层循环 ,复杂度为n*n
//二分 nlogn 少了第二层循环
//因为LIS只需要最长的长度,不需要具体的序列
//所以二分思维巧妙,用的是设置门槛的思路,即不停的让小的替代大的,把门槛降低,
//以达到最后一个数,即真正的序列门槛降低,这样就可以尽可能地增加长度,让更多元素进入序列
//ps 二分中的d[]不是实际上的元素排列,但是它所起到的降低真正门槛的作用,没有问题
//因为他只会降低之后的门槛,也就是说,不会减小当前子序列的长度
例题
dp专题 I - 最少拦截系统 HDU - 1257
挖坑
还没学树状数组维护LIS