题目链接:戳这里
解法一:DP,时间复杂度为O(n^2)
这道题的转移方程非常非常简单,一目了然,先准备一个数组dp
dp[i]=1;代表以dp[i]结尾的最长递增子序列,显然初始值都为1
ps:由于memset只能将数组的值整体初始化为0或者-1,那么初始化为别的值可以用fill函数代替
函数格式:fill(first,last,val) //first 为容器的首迭代器,last为容器的末迭代器,val为将要替换的值。
示例:
int a[200];
fill(a, a+100, 1); //将数组a的前100个元素初始化为1
,接着从a[1]开始搜到i的最长上升子序列。 (图中数组下标从1开始)
dp[2]呢?
如图,它显然比a[1]高,在执行如下语句时
for(j=1;j<i;j++) if(a[i]>a[j])
j小于i,也就是2,目前符合条件的只有a[1],a[1]又通过了判断语句,它确实小于a[i],执行下一条语句:
dp[i]=max(dp[i],dp[j]+1); //状态转换方程
很显然:b[2]显然原来是1,当它和b[1]+1比时,1当然比2小,所以,b[2]自然就是2了。
代码:
#include<iostream>
#include<algorithm>
using namespace std;
int a[1005], dp[1005] = {0}; //dp数组代表以dp[i]结尾的最长递增子序列
int main()
{
int n;
cin >> n;
for (int i = 0; i < n; i++)
{
cin >> a[i];
}
dp[0] = 1; //初始化
for (int i = 1; i < n; i++)
{
dp[i] = 1;
for (int j = 0; j < i; j++) //寻找dp[i]的最大值
{
if (a[i] > a[j])
{
dp[i] = max(dp[i], dp[j] + 1);
}
}
}
int longest = dp[0];
for (int i = 1; i < n; i++)
{
if (dp[i] > longest)longest = dp[i];
}
cout << longest << endl;
return 0;
}
解法二:贪心+二分,时间复杂度O(nlogn)
我们可以模拟一个stack
在有大量数据的情况下,这算法效率极高
但是,怎么来优化程序呢?
我们可以这样来模拟:
每输入一个数,如果这个数大于栈顶的那个数,于是把它推入栈中。
但是,如果这个数大于栈顶呢,这不证明它不可以更新栈中的
某个元素,这时,就可以运用二分查找了。
有人可能会问:这个序列是无序的啊。没错,但查找的是stack里面的元素,而这个栈里的所有元素,都是严格递增的,所以,用二分查找可以把问题缩减为O(nlogn)。
有些不符合逻辑,不是吗?15的下标比17、18、20都大,为什么能插入呢?但是如果仔细想一想,这好像并不影响正常答案,但如果要输出最长上升子序列,那就要改一改这个算法了。
由此,这个查找算法才得以下降到logn,于是,整体也就是O(nlogn)。
具体操作如下:
每次取栈顶元素和读到的元素做比较,如果大于,则将它入栈;如果小于,则二分查找栈中的比它大的第1个数,并替换它。最长序列长度即为最后模拟的大小。
这也是很好理解的,对于i和j,如果i <j且a[i] < a[j],用a[i]替换a[j],长度虽然没有改变但a的'潜力'增大了。
二分查找算法可以用lower_bound函数代替
lower_bound函数的简单解释:
比如说有一个数组a[5]
1 3 5 7 18 --- 数值
0 1 2 3 4 --- 下标
int pos = lower_bound( a,a+5,2)-a;
则说明返回的是 1 ,因为3的下标为1
int pos = lower_bound( a,a+5,6)-a;
则说明返回的是 3,因为7的下标为3
int pos = lower_bound( a,a+5,20)-a;
则说明返回的是 5,因为在这个数组中的所有数字都比20小
代码:
#include<iostream>
#include<algorithm>
using namespace std;
int a[1005],stack[1005]; //stack数组模拟栈
int main()
{
int n;
cin >> n;
for (int i = 0; i < n; i++)
{
cin >> a[i];
}
stack[0] = a[0];
int longest = 1; //最长上升序列长度,同时也作为栈顶“指针”
for (int i = 1; i < n; i++)
{
if (a[i] > stack[longest - 1] ) //大于栈顶元素,则入栈
{
stack[longest++] = a[i];
}
else
{
int replace = lower_bound(stack, stack + longest, a[i])-stack; //代替二分查找,找出第一个大于a[i]的值的下标
stack[replace] = a[i]; //替换
}
}
cout << longest << endl;
return 0;
}