题目大意:
在给出的长度为m的数字序列{a[m]}中,寻找最长的上升子序列,上升子序列是指,该序列中对于任意i<j,都有a[i]<a[j]。
----------------------------------------我是华丽的分割线------------------------------------------
好吧,这一道是最长上升子序列的经典题目,为什么做这道,是我想用我在POJ1088里面学到的东西来做一下。这回是一下就过了,有进步,勉励一下自己!!
当然了,我还是用搜索做的,有了POJ1088的经验呢,发现这个的搜索空间实际上是一个相交的森林。假设每一个节点都是 原来序列中的a[i],
1.所有的父节点的序号值都小于子节点
2.所有的父节点的数值都小于子节点
3.所求的解,就是森林中最高的树的高度。
所以,算法就是,从节点i不断向下搜索(子节点序号>i,且数值>a[i]),直到到达底部节点(没有数据满足子节点的条件了),然后再退回。搜索过程中用一个数组来记录节点到目前为止的最大高度,等遍历完所有的节点以后,最后再从所有节点中选出一个高度最大的节点,它的高度就是节。
不过,这个问题的这个解法还只是递归形式的解法。从上面的表述中发现这个肯定可以用动态规划做的(根据算法导论,上面的解法就是动态规划的第二步骤的实现解法)。
先贴出递归解法:
#include <stdio.h>
#include <stdlib.h>
int *a;
int *c;
int len;
int findMax(int k){
int cMax=0;
int cTemp=0;
int i,aTemp;
aTemp=a[k];
if (c[k]!=-1){ //到过的节点
return c[k];
}
for (i=k+1;i<len;i++){
if (a[i]>aTemp) {
cTemp=findMax(i)+1;
cMax=cTemp>cMax?cTemp:cMax;
}
}
c[k]=cMax;
return c[k];
}
int cmp_fun(const void *a,const void *b){
return *(int *)a-*(int *)b;
}
int main(){
int i;
scanf("%d",&len);
a=(int *)calloc(len,sizeof(int));
c=(int *)calloc(len,sizeof(int));
for (i=0;i<len;i++){
scanf("%d",&(a[i]));
c[i]=-1;
}
for (i=0;i<len;i++){
if (c[i]==-1) findMax(i);
}
qsort(c,len,sizeof(int),cmp_fun);
printf("%d\n",c[len-1]+1);
}
然后是动态规划的解法:自低向上的解法
c[i]=max{ c[k] | if (a[k]<a[i] and k<i) }+1,c数组中代表最大长度。
贴代码:
#include <stdio.h>
#include <stdlib.h>
int *a;
int *c;
int len;
void findMax(){
int cMax;
int cTemp;
int i,j;
for (i=0;i<len;i++){
cMax=0;
for (j=0;j<i;j++){
if (a[j]<a[i]){
cTemp=c[j]+1;
cMax=cMax>cTemp?cMax:cTemp;
}
}
c[i]=cMax;
}
}
int cmp_fun(const void *a,const void *b){
return *(int *)a-*(int *)b;
}
int main(){
int i;
scanf("%d",&len);
a=(int *)calloc(len,sizeof(int));
c=(int *)calloc(len,sizeof(int));
for (i=0;i<len;i++){
scanf("%d",&(a[i]));
c[i]=-1;
}
findMax();
qsort(c,len,sizeof(int),cmp_fun);
printf("%d\n",c[len-1]+1);
}
好了,到此为止。
后来我在查找别人的代码的同时发现了一种用C++的STL multiset实现的方法,参考http://blog.csdn.net/qingniaofy/article/details/7857623 。对于C++不是很熟,没看懂……以后要加油啊……