您的位置:首页 > 其它

最长上升子序列

2015-12-16 21:24 162 查看
最长上升的O(n^2)的方法是我看过最简单的DP了~~

拿POJ 2533来说。
Sample Input
7

1 7 3 5 9 4 8
Sample Output(最长上升/非降子序列的长度)
4
从输入的序列中找出最长的上升子序列(LIS)。
d(i)=max(1,d(j)+1)其中(a[i]>a[j])其中这里的i,j均为下标。
含义是讨论到第i个数的最长上升子序列,要么是1,即都比前面的小,否则一定是由前面某一个dp(j)其中(a[j]<a[i])加1得到.

当然最后得到的总体的最长上升序列的长度为dp数组中的最大值,注意不是最后一个比如2,3,5,1前面3个数,每一个数的dp都是由前面的dp中最大+1继承而来,而最后一个比前面每一个数都小。

#include <stdio.h>
#include <string.h>
int dp[10001];
int a[1001];
int main()
{
int n,m,i,j,max;
while(scanf("%d",&n)==1)
{
memset(a,0,sizeof(a));
memset(dp,0,sizeof(dp));
for (i=1;i<=n;i++)
scanf("%d",&a[i]);
max=0;
for (i=1;i<=n;i++)
{
dp[i]=1;
for (j=1;j<=i-1;j++)
if ((a[j]<a[i])&&(dp[i]<dp[j]+1))//每一次只需要加那个前面dp(j)的最高一次,而如果加了就是dp(i)=dp(j)+1即如果dp(i)如果小于dp(j)+1就要更新,否则肯定保持当前较大的dp(i)
{
dp[i]=dp[j]+1;
}
if (dp[i]>max)
max=dp[i];
}
printf("%d\n",max);
}
return 0;

}

这个解法不是我想的,从网上学来的,这里面与大家分享一下。这个算法的复杂度只有O(nlogn),在有大量数据的情况下,这算法效率极高。。。

(摘录原作者的话)
这个算法其实已经不是DP了,有点像贪心。至于复杂度降低其实是因为这个算法里面用到了二分搜索。本来有N个数要处理是O(n),每次计算要查找N次还是O(n),一共就是O(n^2);现在搜索换成了O(logn)的二分搜索,总的复杂度就变为O(nlogn)了。
这个算法的具体操作如下(by RyanWang):
开一个栈,每次取栈顶元素top和读到的元素temp做比较,如果temp > top 则将temp入栈;如果temp < top则二分查找栈中的比temp大的第1个数,并用temp替换它。 最长序列长度即为栈的大小top。
这也是很好理解的,对于x和y,如果x < y且Stack[y] < Stack[x],用Stack[x]替换Stack[y],此时的最长序列长度没有改变但序列Q的''潜力''增大了。
举例:原序列为1,5,8,3,6,7
栈为1,5,8,此时读到3,用3替换5,得到1,3,8; 再读6,用6替换8,得到1,3,6;再读7,得到最终栈为1,3,6,7。最长递增子序列为长度4。
#include <stdio.h>
int s[1001];
int main()
{

int i,n,top,temp;
while(scanf("%d",&n)==1)
{
top=0;
s[top]=-1;//保证最小
for (i=1;i<=n;i++)
{
scanf("%d",&temp);
if (temp>s[top])//如果比栈顶元素大则入栈成为栈顶元素
s[++top]=temp;
else//否则二分查找比temp大的第一个数
{
int low=1;
int high=top;
int middle;
while(low<=high)
{
middle=(low+high)/2;
if (s[middle]<temp)
low=middle+1;
else
high=middle-1;
}
s[low]=temp;//用temp代替比temp大的第一个数代替,总数不变
}
}
printf("%d\n",top);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: