#include
#include
using namespace std;
int num[10001];
int sum[10001];
int dp[10001]={0};
int main()
{
int n;
while(cin>>n)
{
if(n==0) break;
memset(num,0,sizeof(num));
memset(dp,0,sizeof(dp));
for(int i=1;i<=n;i++)
{
cin>>num[i];
dp[i]=num[i];
}
for(int i=1;i<=n;i++)
{
for(int j=1;jnum[j]&&dp[i]
总结:
最长上升子序列二分方法的思想(n*log(n))//利用二分将复杂度降低
用f【i】来保存的是长度为i 的最长上升子序列末位所能达到的最小值
注意 这里的i 是随着访问原数组不断进行更新的 它的最大值(即f 数组的大小 )就是最长上升子序列的长度
当我们访问到原数组(假如原数组是num[]),当访问到num[j] 时,原序列进行二分查找,目的是看是否存在一个值k 满足{f(k)
二分方法只能用来找到最长上升子序列的长度 不能将最长上升子序列元素输出出来
F序列中记载的并不是真正的最长上升子序列,但其长度等同于最长上升子序列的长度。
最简单的理解是,由于我们对原数组都是依次进行访问的,如果当前的值num[正在访问]比f【已经存下】的所有元素的值都大,我们就放入,如果比f中的某个元素小,就将其对应代替,那么如果比f{所有元素}大就放,比f{所有元素}小就替换,访问到最后一个num[最后一个]时,f的长度正好就是最长公共子序列的长度~
#include
int z[1007],dp[1007];
int main()
{
int n,i,j;
scanf("%d",&n);
for(i=1; i<=n; i++)
{
scanf("%d",&z[i]);
}
dp[1]=z[1];
int left,right,lenth=1;
for(j=2; j<=n; j++)
{
left=1;
right=lenth;
while(left<=right)
{
int mid=(left+right)/2;
if(dp[mid]lenth)
lenth++;
}
printf("%d\n",lenth);
return 0;
}
还有一个(n^2)的方法,也是基础方法:
它的思想是dp[i] 中保存的是1~i这段中 最长子序列的长度
dp[i]=max(dp[j])+1;
i>j,num[i]>num[j];
从给出的状态转移方程就可以看出,访问到num[i]时,不断用num【1~j】和num[i]相比较,如果小于num[i],那么当前的dp[i]肯定就是在dp[j]的基础之上加1
if(n>=10000)
You can't finish it in less than a second
剩下的就是对代码的熟练度了~