【牛客】45thICPC-F-Kobolds and Catacombs 题解

传送门:第45届ICPC沈阳站F题
标签:动态规划

题目大意

给定一个长度为n的序列(1<=n<=1e6,1<=ai<=1e9),你可以对其进行多次以下操作:将该序列中任意连续的一段构成一个组。要求最终原序列每个数都在组内,且在对各个组内的元素进行升序排列后,整个序列都呈升序排列。求:在满足条件的情况下最多能构成多少组?
输入:n,ai(i=1,2,3···n)
输出:组数的最大值

算法分析

  • 乍看一下似乎是贪心,但还是要先模拟才能得到结论。首先可以确定一点:将原序列升序排列得到的结果是唯一的,也就是说不管怎么分组,原序列第k大的数最终一定会在下标为k的位置。那么我们可能会想到先把复制一个原序列,sort后得到升序序列,再找每个数在原序列里的位置,最后贪心得到答案。很显然这样做是错的,一是因为时间复杂度太大,二是因为序列中同一个数可以出现多次。
  • 我们不妨从组与组之间的关系入手。手捏几个数据观察后不难发现,后一个组的最小值一定大于或等于前一个组的最大值。再分析一下题意:既然只能对在同一组内的元素进行排序,还要使排序后整体递增,那么显然各组之间是整体递增的。比如样例的数据:1 3 2 7 4,分组后变成了[1] [3 2] [7 4],3是序列中第3大的数,整体排序后就在第3位,依然在原来的组内。既然各个组是相互独立的,那么排在后面的组的每一个元素都要大于前面组的元素。
  • 有了结论,我们就要考虑如何实现了。如果学过动态规划就会很快发现:这是最长上升子序列的变式!建立每一个组时,如果下一个数比上一个组的最大值大且比当前组的最大值大,就开一个新的组;如果比上一个组的最大值大但比当前组的最大值小,就留在这个组;而如果比上一个组的最大值小,就二分找前面最大值比当前数大的第一个组并替换为当前组最大值,这样就能以最大nlogn的时间复杂度解决这题。

代码实现

#include <iostream>
using namespace std;
#include <cstring>
int maxs[1000001]={0};
int main(){
	int i,x,l,r,mid,n,top=1;
	std::ios::sync_with_stdio(false);
	std::cin.tie(0);std::cout.tie(0);
	cin>>n;		//序列元素数 
	for(i=0;i<n;i++){
		cin>>x;
		if(maxs[top-1]<=x)	//比上一个组最大值大 
			if(maxs[top]<=x)maxs[++top]=x;	//比当前组最大值大 
			else ;	//防止if和else匹配错误,所以加了个else 
		else{
			l=2;r=top-1;	//二分找比当前数大的第一个数 
			while(l<r){
				mid=(l+r)/2;
				if(maxs[mid]<=x)l=mid+1;
				else r=mid;
			}
			maxs[l]=maxs[top];	//替换最大值 
			top=l;	//更新组数 
		}
	}
	cout<<top-1;	//输出答案 
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值