洛谷 - LIS (最大上升子序列模板题) (dp,二分,贪心)

题目链接
题目:
在这里插入图片描述

思路:
这是最大上升子序列的模板题,为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);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值