示例题目:POJ2533
参考书籍:《挑战程序设计竞赛》
第一种方法:O(n^2)
dp[i]:以a[i]结尾的最长上升子序列的长度
- 只包含a[i]的序列
- 由a[j]追加a[i]得到(满足j< i并且a[j]< a[i])
状态转移方程:
dp[i]=max{dp[i],dp[j]+1}
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
int a[1005];
int dp[1005];
int main()
{
ios::sync_with_stdio(false);
int n;
while(cin>>n)
{
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
memset(dp,0,sizeof(dp));
int res=0;
for(int i=1;i<=n;i++)
{
dp[i]=1;
for(int j=1;j<i;j++)
{
if(a[i]>a[j])
dp[i]=max(dp[i],dp[j]+1);
}
res=max(dp[i],res);
}
cout<<res<<endl;
}
return 0;
}
第二种方法:O(nlogn)
dp[i]:长度为i+1的上升子序列中末尾元素的最小值(不存在就是INF)
状态转移方程:
对数列从左往右扫一遍,对于当前扫到的元素a[j]
如果i=0或者dp[i-1]< a[j],就用dp[i]=min(dp[i],a[j])更新,
考虑到dp数列中除INF外是单调递增的,dp[i]的指针可由
lower_bound(dp,dp+n,a[j])得出。
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int INF=0x3f3f3f3f;
int a[1005];
int dp[1005];
/// dp[i]表示长度为i+1的上升子序列中末尾元素的最小值
/// 不存在就是INF
int main()
{
//int t[5]={1,2,4,4,5};
//cout<<lower_bound(t,t+5,3)-t<<endl;
ios::sync_with_stdio(false);
int n;
while(cin>>n)
{
for(int i=0;i<n;i++)
{
cin>>a[i];
}
fill(dp,dp+n,INF);
for(int i=0;i<n;i++)
{
*(lower_bound(dp,dp+n,a[i]))=a[i];
}
cout<<lower_bound(dp,dp+n,INF)-dp<<endl;
}
return 0;
}
按照白书上的理解
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int INF=0x3f3f3f3f;
int a[1005];
int dp[1005];//dp[i]:以a[i]结尾的LIS的长度
int g[1005];//g[k]=a[j] (dp[j]=k&&j最小)
///g[1]<=g[2]<=g[3]<=...<=g[n] (if not exist g[i]=INF)
///dp[i]=max(0,dp[j])+1 (j<i&&a[j]<a[i])
int main()
{
int n;
while(cin>>n)
{
for(int i=0;i<n;i++)
cin>>a[i];
fill(g,g+n+1,INF);
for(int i=0;i<n;i++)
{
//g[k']<a[i]<=g[k] (k=k'+1)
int k=lower_bound(g+1,g+n+1,a[i])-g;
//g[k']=a[j']<a[i] dp[j']=k'=k-1
//g[k]=a[j]>=a[i]
dp[i]=k;
//dp[i]=max(0,dp[j'])+1=k (j'<i&&a[j']<a[i])
g[k]=a[i];
}
cout<<lower_bound(g+1,g+n+1,INF)-(g+1)<<endl;
}
return 0;
}
1431

被折叠的 条评论
为什么被折叠?



