题目:
有一个长为n的数列a0,a1,...,an-1。请求出这个序列中最长的上升子序列的长度。上升子序列指的是对于仍以的 i<j 都满足ai<aj 的子序列。1 <= n <= 1000 ,0<= ai <= 1000000。
输入样例:5 4 2 3 1 5
输出样例:
3 (a1,a2,a4构成子序列2,3,5最长)
思路1:
这个问题是被称为最长子序列。可以通过DP的方法来求解。
定义dp[i]为以ai为末尾的最长上升子序列的长度。以ai为结尾的上升子序列有两种情况
1,只包含,ai
2,包含一个以aj为结尾的上升子序列,再追加上 ai,其中 aj<ai。
所以可以写出地推关系:
dp[i]=max{1,dp[j]+1 | j<i 且 aj<ai}
通过该方法的时间复杂度为 O(n^2).
int n;
int a[MAXN];
int dp[MAXN];
void solve()
{
int ans=0;
for(int i=0;i<n;i++)
{
dp[i]=1;
for(int j=0;j<i;j++)
{
if (a[j]<a[i])
{
dp[i]=max(dp[i],dp[j]+1);
}
ans=max(anx,dp[i]);
}
}
cout<<ans<<endl;
}
思路2:
通过二分搜索的方法实现。
首先,思考这样一个问题:对于数列p={1,2,4,3,5,6,7},在中间某一步计算结束后,我们得到了两个长度为3的上升子序列,分别为,p1={1,2,3},p2={3,5,6}。请问p1和p2哪个更加有“潜力”,显然p1的潜力更大一些,因为p1中最大的数字比p2中的数字小。
所以可以通过栈的思想来做,在依次遍历数列的过程中,当遍历到数字ai的时候,当ai>top的时候,ai进栈,成为新的top。当ai<top的时候,二分查找栈中元素,找出第1个比ai大的数字,比讲它替换,得到新的栈。该栈中元素的个数就是最长上升序列的个数。
数列:1,5,7,2, 3,6
栈中元素: 1,5,7。2进栈替换5,变成,1,2,7。同理3替换7,得到:1,2,3。最后6进栈,得到,1,2,3,6。最长子序列为4
该方法的时间复杂度是,nlogn。缺点是,不能得到正确的序列。
void solve()
{
int i, j, n, top, temp;
int arr[1010];//用数组代替栈
cin >> n;
top = 0;
arr[0] = -1;
for (i = 0; i < n; i++)
{
cin >> temp;
if (temp > arr[top])
{
arr[++top] = temp;
}
else//二分查找
{
int low = 1, high = top;
int mid;
while(low <= high)
{
mid = (low + high) / 2;
if (temp > arr[mid])
{
low = mid + 1;
}
else
{
high = mid - 1;
}
}
arr[low] = temp;
}
}
cout << top << endl;
}