3 1 3 2 4 1 2 3 4 4 3 3 2 1 0
4 10 3
#include<iostream> #include<string.h> 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;j<i;j++) { if(num[i]>num[j]&&dp[i]<dp[j]+num[i]) dp[i]=dp[j]+num[i]; } } int max=0; for(int i=1;i<=n;i++) { if(max<dp[i]) max=dp[i]; } cout<<max<<endl; } return 0; } hdu 1087
总结:
最长上升子序列二分方法的思想(n*log(n))//利用二分将复杂度降低
用f【i】来保存的是长度为i 的最长上升子序列末位所能达到的最小值
注意 这里的i 是随着访问原数组不断进行更新的 它的最大值(即f 数组的大小 )就是最长上升子序列的长度
当我们访问到原数组(假如原数组是num[]),当访问到num[j] 时,原序列进行二分查找,目的是看是否存在一个值k 满足{f(k)<num[j]<=f[k+1];}(就是找到了比f[]中所有值都大的数,则要将其放入f[]中) 若存在,则f[k+1]=num[j],同时f的长度加一 如果不存在,替换相应的值,继续向后访问~
二分方法只能用来找到最长上升子序列的长度 不能将最长上升子序列元素输出出来
F序列中记载的并不是真正的最长上升子序列,但其长度等同于最长上升子序列的长度。
最简单的理解是,由于我们对原数组都是依次进行访问的,如果当前的值num[正在访问]比f【已经存下】的所有元素的值都大,我们就放入,如果比f中的某个元素小,就将其对应代替,那么如果比f{所有元素}大就放,比f{所有元素}小就替换,访问到最后一个num[最后一个]时,f的长度正好就是最长公共子序列的长度~
#include<stdio.h> 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]<z[j]) { left=mid+1; } else right=mid-1; } dp[left]=z[j]; if(left>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
剩下的就是对代码的熟练度了~