标签:
最长上升子序列问题:
给出一个由n个数组成的序列x[1..n],找出它的最长单调上升子序列。即求最大的m和a1,
a2……,am,使得a1<a2<……<am且x[a1]<x[a2]<……<x[am]。
1.动态规划求解思路分析:(O(n^2))
经典的O(n^2)的动态规划算法,设a[i]表示序列中的第i个数,b[i]表示从1到i这一段中以i结尾的最长上升子序列的长度,初始时设F[i] = 0(i = 1, 2, ..., len(a))。则有动态规划方程:b[i] = max{1, b[j] + 1} (更新,最大的加一)(j = 1, 2, ..., i - 1, 且a[j] < a[i])。
令a[i]表示输入第i个元素,b[i]表示从a[1]到a[i]中以a[i]结尾的最长子序列长度。对于任意的0 < j <= i-1,如果a(j) < a(i),则a(i)可以接在a(j)后面形成一个以a(i)结尾的新的最长上升子序列。对于所有的 0 < j <= i-1,我们需要找出其中的最大值。
DP状态转移方程:
b[i] = max{1, b[j] + 1} (j = 1, 2, 3, ..., i-1 且 a[j] < a[i])
解释一下这个方程,i, j在范围内:
如果 a[j] < a[i] ,则b[i] = b[j] + 1
如果 a[j] >= a[i] ,则b[i] = 1
1 #include <stdio.h> 2 int a[10000],b[10000]; 3 int main() 4 { 5 int n, i, j, max; 6 scanf("%d", &n); 7 for(i=0;i<n;i++) 8 { 9 scanf("%d", &a[i]); 10 b[i] = 1; 11 } 12 for(i=n-1;i>=0;i--) 13 for(j=i;j<n;j++) 14 if(a[i]<a[j] && b[j]+1>b[i]) 15 b[i] = b[j]+1; 16 for(max=1,i=0;i<n;i++) 17 if(max < b[i]) 18 max = b[i]; 19 printf("%d\n", max); 20 return 0; 21 }
2.贪心+二分查找:(O(nlogn))
开辟一个栈,每次取栈顶元素s和读到的元素a做比较,如果a>s,则加入栈;
如果a<s,则二分查找栈中的比a大的第1个数,并替换。
最后序列长度为栈的长度。
这也是很好理解的,对x和y,如果x<y且E[y]<E[x],用E[x]替换 E[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 f(int a[], int n) { int b[10100]; int t, i, z ,y, mid; b[t=1] = a[0]; for(i=1;i<n;i++) { if(a[i]>b[t]) b[++t] = a[i]; else { z=0;y=t; while(z<y) { mid = (z+y)/2; if(b[mid] > a[i]) y = mid; else z = mid+1; } b[z] = a[i]; } } return t; } int main() { int a[100011]; int n, i, k; scanf("%d", &n); for(i=0;i<n;i++) scanf("%d", &a[i]); printf("%d\n", f(a, n)); return 0; }
推荐一些最大上升序列的题
poj:2533 http://poj.org/problem?id=2533
题解:
标签:
原文地址:http://www.cnblogs.com/Noevon/p/5685975.html