雄关漫道真如铁,而今DP从头越。
没想到自己原来这么脆,连最初的最长上升子序列都不会。。。。QAQ
明明一年前学长就跟我们讲过了,自己也没多在意,后来比赛中无意中出现了,自己却没有能力实现解决。
真的很抱歉,只能临时套模板来解决。
好了,重新开始吧,不怕自己不会,只怕自己不想学。
什么是最长上升子序列? 就是给你一个序列,请你在其中求出一段不断严格上升的部分,它不一定要连续。
就像这样:2,3,4,7和2,3,4,6就是序列2 5 3 4 1 7 6的两种选取方案。最长的长度是4.
那么,怎么求出它的最大上升子序列长度为4呢?这里介绍两种方法,都是以动态规划为基础的。
首先,我们先介绍较慢(O(n2n2))的方法。我们记num为到这个数为止,最长上升子序列的长度。
这种方法就是每一次寻找“可以接下去的”,换句话说,设原序列为a,则
当 aj
对于每一个数,他都是在“可以接下去”的中,从前面的最优值+1转移而来。
因此,这个算法是可以求出正确答案的。复杂度很明显,外层i枚举每个数,内层j枚举目前i的最优值,即O(n^2)。
那么,有没有更快的方法呢?当然有。这回要用到二分。
我们回想一下,在上面O(n^2)的程序中,哪些地方看起来比较费时?
没错,就是内层用于更新i的循环。因为每一次他都要查找一遍,效率并不高。
回到题目,我们发现,他只要我们求长度,所以?
我们可以模拟一个队列。
所以每遇到一个比队尾元素大的数,就放进队伍后面里,遇到比队头元素大的就二分查找前边的元素,找到一个“最应该被换掉的元素”,用新数去更新前边的元素。
这个算法不难证明也是正确的。因为前面每一次的枚举都换成了二分,内层的复杂度从n降到了log2,外层不变。所以总的复杂度是O(nlog2n)。
根据引例而写的,里面有必要的注释。
#include
using namespace std;
int main()
{
int a[8]={0,2,5,3,4,1,7,6};
int n=7;
int dp[10]={0};
dp[1]=1;
int ans=0;
for(int i=2;i<=n;i++){
for(int j=i-1;j>=0;j--){
if(a[j]
#include
using namespace std;
const int N=1e2;
const int MAX=0x3f3f3f3f,MIN=-0x3f3f3f3f;
int main()
{
int a[N]={0,2,5,3,4,1,7,6},n=7;
int dp[N];
int top=1;
dp[1]=a[1];
//printf("%d %d\n",MAX,MIN);
for(int i=2;i<=n;i++){
if( a[i] < MIN || a[i] > MAX ){ continue; } //判断数据的合法性
if( a[i] < dp[1] ){ //找到在序列中最小值还小的值
dp[1] = a[i]; //替换其最小值
}else if( a[i] > dp[top]){//找到比序列中最大值还大的值
dp[++top] = a[i]; //加入到序列中,并位于最后。
}else{ //找到序列 中等水平
int pos=lower_bound(dp+1,dp+1+top,a[i])-dp;
//找到对应的位置改变中等水平
dp[pos]=a[i]; //将其替代
}
}
printf("%d\n",top); //LIS=该序列的长度
return 0;
}
/*
for(int i=2;i<=n;i++){
if( a[i] < MIN || a[i] > MAX ){ continue; } //判断数据的合法性
if( a[i] < dp[1] ){ //找到在序列中最小值还小的值
dp[1] = a[i]; //替换其最小值
}else if( a[i] >= dp[top]){//找到比序列中最大值大于等于的值
dp[++top] = a[i]; //加入到序列中,并位于最后。
}else{ //找到序列 中等水平
int pos=upper_bound(dp+1,dp+1+top,a[i])-dp;
//找到对应的位置改变中等水平
dp[pos]=a[i]; //将其替代
}
}
printf("%d\n",top); //LIS=该序列的长度
*/
#include
using namespace std;
const int MAX=0x3f3f3f3f,MIN=-0x3f3f3f3f;
const int N=1e2;
void solve1(int a[],int n){
int *dp;
dp=(int *)malloc(sizeof(int)*(n+2));
dp[1]=a[1];
int top=1;
for(int i=2;i<=n;i++){
if( a[i]MAX ) continue;
if( a[i] < dp[1] ) {
dp[1]=a[i];
}else if( dp[top] < a[i] ){
dp[++top]=a[i];
}else{
int pos=lower_bound(dp+1,dp+1+top,a[i])-dp;
dp[pos]=a[i];
}
}
printf("%d\n",top);
for(int i=1;i<=top;i++){
printf("%d%c",dp[i],i==top?'\n':' ');
}
}
void solve2(int a[],int n){
int *dp;
dp=(int *)malloc(sizeof(int)*(n+2));
dp[1]=a[1];
int top=1;
for(int i=2;i<=n;i++){
if( a[i]MAX ) continue;
if( a[i] < dp[1] ) {
dp[1]=a[i];
}else if( dp[top] <= a[i] ){
dp[++top]=a[i];
}else{
int pos=upper_bound(dp+1,dp+1+top,a[i])-dp;
dp[pos]=a[i];
}
}
printf("%d\n",top);
for(int i=1;i<=top;i++){
printf("%d%c",dp[i],i==top?'\n':' ');
}
}
int main()
{
int a[N]={0,2,5,3,4,1,7,7,6};
int b[N]={0,2,5,3,3,4,1,7,6};
int n=8;
printf("严格递增子序列:\n");
solve1(a,n);
solve1(b,n);
printf("不递减上升子序列:\n");
solve2(a,n);
solve2(b,n);
return 0;
}