P3902 递增

递增

传送门:

P3902 递增 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

思路

刚开始看到这道题以为是求出最长上升子列直接减一下,

感觉是暴力秒杀

但是这道题的数据范围最多可以a50分

那么我们就要思考解决方法了

首先数的范围是到1e9,但是数最多只有1e5个所以可以考虑离散化(这叫不叫离散化蒟蒻也不太清楚)

那我们应该怎么离散化呢

这是个关键点!这里我们把dp的下标当作当前数的最长子序列

dp中的元素表示当前数

那么每次如果我下一个元素大于当前元素我是不是可以下个元素的长度是不是就等于前一个元素长度+1

如果不等于那么我们可以查找一个正好小于的情况,然后更新当前长度下的数

(这样复杂度要小于N^2但是还是差不多,所以我们还需要优化,优化过会提)

我们继续来思考为什么要这样做,这样做的依据是什么?

依据是没有依据……

因为每次都是当前最大的,如果当前数大于上个数,那么原长度+1是不是就是当前最大的了(有一点贪心的思想);如果当前不是最大的那么就找一个最大的。

错误……错误,我一开始也是这样想的但是其实是想错了

就是每次找一个比自己的数小的但是又是当前长度最长的,+1就是我要的当前数的最长递增子序列

那么其实我只需要优化来找的过程就能解决了

要查找了O(N)肯定不显示,O(N^2)肯定超时,一般O(NlogN)一想就是二分

为了方便思考,其他思路写在代码注释上

有问题请评论或私信我,谢谢!

AC code

// Problem: 
//     P3902 递增
//   
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P3902
// Memory Limit: 125 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<iostream>
#include<algorithm>
//#include<cstdio>

#define ll long long 
#define endl '\n'
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
#define N 100010 //1e6+100 
 
using namespace std;
int n,res;
double a[N],dp[N];
int main(){
	cin>>n;
	rep(i,1,n) cin>>a[i];
	rep(i,1,n){
		if(a[i]>dp[res]){
//如果当前数比当前最长子序列的最后一个数要大,那么新的最长子序列长度就是这个子序列的长度+1,而当前最长子序列的最后一个数就是这个数
			res++;//当前最长子序列长度=原来最长子序列长度+1
			dp[res]=a[i];//当前最后一个数是这个数
		}else{
		//为什么可以用二分,首先dp是一个递增序列,
		//因为每次赋值dp的时候都是当a[i]>dp[res]的时候把a[i]赋值给dp[res+1]的
		//其次更新的时候,每次是把第一个a[i]<=dp[]的位置更新成a[i]后面本来就是>=a[i]的数我更新的当前位置再小是>=a[i]的所以他肯定也是不改变增减性的,为什么这样更往下看;
		//递增讲完了,那么讲讲为什么这样更的话可以把每个数放好使这个数成为本属于他当前子序列的最后一个数
		//在思路中提过dp的下标就表示当前最长子序列长度
		//那么是不是只要大于某个最长子序列的最后一个数的后一个元素(当然前提是递增),那这个后一个元素就是当前数可以组成的最长子序列了。
			int l=0,r=res+1;
			while(l+1<r){
			//这种二分板子可能比较少有人用,那可以用自己的板子,或者用 lower_bound()函数
			//只要保证我们的找dp中大于等于a[i]的数就行

				int mid=(l+r)/2;
				if(a[i]>dp[mid])l=mid;
				else r=mid; 
			}
			//l是比当前数小的哪一个元素,那么r也就是l+1就是这个属于这个数的最长子序列长度
			dp[r]=a[i];//更新当前最长子序列下的最后一个数
			//一定要更新,因为如果不更新的话比如原来dp[r]的位置是5然后a[i]是4后面还有一个a[i]是5,那么如果不更新a[i]为5的那个数最长上升子序列就是r了不会是正确的值(就小了),更改了之后就会变正常
		}
	}
	cout<<n-res;//题目是至少需要改几个数使他变为递增序列,那么我们只要n-最长子序列长度就行
	return 0;
}




有问题请评论或私信我,谢谢!

有共同学习需求的可以加入洛谷团队:https://www.luogu.com.cn/team/66731

  • 7
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值