https://vjudge.net/problem/HDU-4604
给定一排数和一个 双头队列。
可以往头插还可以往后插的,问你最大得到的 非递减队列的长度。
经过分析,枚举每个点作为开头的 最大非严格递增和 最大非严格递减子序列,然后相加 减去他们 中重叠的数。
重叠的数是这样计算的。分别计算一个数在 两个数列中的数量,然后取最大的那一个
前天才看懂代码的意思,被一个地方卡了。
首先,
a=upper_bound(a,a+n,x)-a;
b=lower_bound(a,a+n,x)-a;
1 a-b+2 和 upper_bound()-lower_bound()其实是一样的。。
只是写法不同,都是计算那个x在a[]中的数量。。
2 别人一句话就给我说明白了。lis本来是求的dp[]是以i为末尾的长度。如果反响求,并且递增改成递减,就可以求以头为长度的了。
并且要 注意 差别,递增和非递降有点差别,
如果用的是a,即每次插入都是在第一个大于他的数,那就是非递降啊(相同的数得以保存,数组中假设有一个 1 2 3 ,那么再插入一个3,还是可以插在最后面的,哈哈两个3 了如果这样。)
但是如果是用b呢,那么就还是得到 12 3 ,还是一个3,、
就这点不一样。
4 另外我感觉还是应该锻炼自己的思考能力,多思考基本的dp结构。
这种题可以加强对lis的理解。
6 我第一次吧name初始化为1,然后求最大的也过了。突然我发现。
两个数列中 同一个数的存在个数肯定是相同的。
求最大的和最小的都是一样的。除非一个 正常递增,一个非严格递减,然后出现不同的情况,不过这种情况用本题的代码也可以算。
如果求最小的就初始化为0x3f3fl
第一次反转,求的是以一个点为首的下降子序列(但是位置不是以前的位置,即第一个数其实是原数组中倒数第一个数的)
在反转的基础上又进行了 取负值,求的才是以这个数的上升,不过也是相反的位置,和上一个一个,故进行加减不会造成损失
#include <bits/stdc++.h>
using namespace std;
/*先求一个 递降(非标准递降,就是不递升)
然后再求一次递升,(非标准递升,就是不递降)
本来lis计算的是为最后的数,倒着计算就是为头的数。
该球递降的求递升,该求递升的求递降
二者相反,但由于这道题二者都需要计算,所以就不用了 qwq
*/
const int maxn=100006;
int a[maxn];
int num[maxn];
int n;
int solve(int dp[])
{ vector<int>q;//设置一个队列来储存这个序列.
for(int i=0;i<n;i++)
{ int a1=upper_bound(q.begin(),q.end(),a[i])-q.begin();//用来计算第一个比他大的
int b1=lower_bound(q.begin(),q.end(),a[i])-q.begin();
if(a1==q.size())
{ q.push_back(a[i]);
dp[i]=q.size();
}
else
{ q[a1]=a[i];//把第一个大于的数给换掉
dp[i]=a1+1;
}
num[i]=min(num[i],a1-b1+1);//或者初始为1,求最大
}
return 0;
}
int main()
{ std::ios::sync_with_stdio(false);
int dp1[maxn];
int dp2[maxn];
int t;
cin>>t;
while(t--)
{ memset(dp1,0,sizeof(dp1));
memset(dp2,0,sizeof(dp2));
memset(num,0,sizeof(num));
memset(a,0,sizeof(a));
scanf("%d",&n);
for(int i=0;i<n;i++)
num[i]=0x3f3f3f3f;//为什么要设置成0呢,因为最少一个数也是0啊。。
for(int i=0;i<n;i++)
scanf("%d",&a[i]);
for(int i=0;i<n;i++)
reverse(a,a+n);
solve(dp1);
for(int i=0;i<n;i++)
a[i]=-a[i];
solve(dp2);
int all=0;
for(int i=0;i<n;i++)
all=max(all,dp1[i]+dp2[i]-num[i]);
printf("%d\n",all);
}
return 0;
}