题目链接
题目:
思路:
这是最大上升子序列的模板题,为dp思想。
dp动态方程为: 当a[i] 大于 a[j] 时 f[i] = max( f[j] + 1 , f[i] ),其中f[i]表示的是以这个数结尾的最长子序列,a[i]表示的是i这个位置的数。
dp代码:
#include<iostream>
#include<cstdio>
#define ll long long
#define rg register int
using namespace std;
int n,ans;
int a[10005];
int f[10005];
int main()
{
cin>>n;
for(int i=1;i<=n;++i) cin>>a[i];
for(int i=1;i<=n;++i)
{
for(int j=1;j<i;++j)
if(a[j]<a[i])
f[i]=max(f[i],f[j]+1);//动态转移方程
ans=max(ans,f[i]);
}
cout<<ans<<endl;
return 0;
}
但是我们注意到了,这里的n为1e5,数据太大,会t掉。
这里于是有了一种,二分加贪心的算法。应该也可以叫做,dp思想维护栈。
1.当栈顶的元素小于当前枚举到的a[i],那么直接加入栈中。因为这时候的a[i]比结尾的元素大,可以直接加入。
2.当栈顶元素大于这时候的a[i]的时候,a[i]不能放在末尾了,但是题中又要求不能改变顺序的放置元素,但是在这里,我们可以在栈中二分查找到第一个大于a[i]元素,然后替换他,(贪心,同一个位置,数肯定越小越好,后面放的元素才有更多种可能,但是是否会打乱顺序呢?答案是不会,这里我们我们可以想一想,如果第一个大于他的元素为栈顶,那么就直接把栈顶替代了,观察一下,并没有打乱什么顺序,而没有替换栈顶的时候,其实替换后,看起来是改变了顺序,但是实质上,这个数我们只是看做了替换了一下,但是我们可以又看做不替换,这里实在难解释,大家可以想一想)
总结一句话,栈中的序列,未必是真正意义上的最长子序列,而是求出了他的真正长度,如果题中要求求出最长子序列的序列,那么这个方法就不行。
AC代码
#include <bits/stdc++.h>
inline int read(){char c = getchar();int x = 0,s = 1;
while(c < '0' || c > '9') {if(c == '-') s = -1;c = getchar();}
while(c >= '0' && c <= '9') {x = x*10 + c -'0';c = getchar();}
return x*s;}
using namespace std;
#define NewNode (TreeNode *)malloc(sizeof(TreeNode))
#define Mem(a,b) memset(a,b,sizeof(a))
const int N = 1e5 + 5;
const long long INFINF = 0x7f7f7f7f7f7f7f;
const int INF = 0x3f3f3f3f;
const double EPS = 1e-7;
const unsigned long long mod = 998244353;
const double II = acos(-1);
const double PP = (II*1.0)/(180.00);
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef pair<ll,ll> piil;
int Top[N],arr[N];
int main()
{
std::ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
int n,top = 0,a;
scanf("%d",&n);
for(int i = 1;i <= n;i++) arr[i] = read();
for(int i = 1;i <= n;i++)
{
if(Top[top] < arr[i]) Top[++top] = arr[i];
else Top[lower_bound(Top+1,Top+top+1,arr[i])-Top] = arr[i];
}
printf("%d\n",top);
}