题目链接
题目描述与示例
牛牛定义排序子序列为一个数组中一段连续的子序列,并且这段子序列是非递增或者非递减排序的。牛牛有一个长度为n的整数数组A,他现在有一个任务是把数组A分为若干段排序子序列,牛牛想知道他最少可以把这个数组分为几段排序子序列.
如样例所示,牛牛可以把数组A划分为[1,2,3]和[2,2,1]两个排序子序列,至少需要划分为2个排序子序列,所以输出2
输入描述
输入的第一行为一个正整数n(1 ≤ n ≤ 10^5) 第二行包括n个整数A_i(1 ≤ A_i ≤ 10^9),表示数组A的每个数字。
输出描述
输出一个整数表示牛牛可以将A最少划分为多少段排序子序列
示例
输入:
6 1 2 3 2 2 1
输出:
2
解题思路
题目分析
题目还是比较简单的,就相当于给你一个数组,然后你按规则把这个数组划分,最少可以分几段即可。需要注意的地方就在于需要划分出来的排序子序列是一个非递增排序或者非递减排序。
非递增(递减)排序我感觉说简单点就是在单调递增(递减)排序的基础上允许中间元素出现重复,就比如1 2 3单调递增,那么1 2 2 3即使个非递减排序。
个人思路
- 开始遍历数组时判断下一个元素与首元素的大小关系,确定即将进入非递增(非递减)排序
- 调用对应函数找到结束非递增(非递减)排序的位置下标
- count++
我感觉不太会描述我自己的思路了,给个图理解下吧,就拿题目给的示例来看:
函数逻辑比较简单,匹配到下一个数据不再大于等于(小于等于)就表示找到了非递减(非递增)的位置:
for循环的i++后会自动指向下一个元素进行新一轮判断直到完全遍历数组为止。
这个地方需要注意的是,当下一个元素和当前元素相同的时候,无论是划分到非递增或者非递减都是一样的,所以相同元素只需要执行i++即可无需count++。
代码展示
#include<iostream>
#include<vector>
using namespace std;
//找到非递减排序结束的下标
int IsASC(const vector<int> &A,size_t begin)
{
size_t end=begin;
for(;end<A.size();end++)
{
if(A[end+1]<=A[end])
break;
}
return end;
}
//找到非递增排序结束的下标
int IsDESC(const vector<int> &A,size_t begin)
{
size_t end=begin;
for(;end<A.size();end++)
{
if(A[end+1]>=A[end])
break;
}
return end;
}
int main()
{
int n;
vector<int> A;
cin>>n;
int tmp;
for(int i=0;i<n;i++)
{
cin>>tmp;
A.push_back(tmp);
}
int count=0;
for(size_t i=0;i<A.size();i++)
{
//相等的话无需寻找结束标志,i++即可
if(A[i+1]==A[i])
{
continue;
}
//下一个元素大于当前元素表明即将进入非递减排序
if(A[i+1]>A[i])
{
//找到结束非递减排序的下标后修改i的值
//这个地方一定要修改,不然会导致这个子序列的所有字符都会进行一次划分
i=IsASC(A,i);
count++;
continue;
}
if(A[i+1]<A[i])
{
//同上
i=IsDESC(A,i);
count++;
}
}
cout<<count;
return 0;
}
题解
本次我的思路与题解几乎一致,但是在实现上却显得复杂了很多,也有些小细节没有处理好,还是先给出题解代码:
#include<iostream>
#include<vector>
using namespace std;
int main()
{
int n;
cin>>n;
vector<int> a;
//因为判断时都是拿下一个元素与当前元素做比较
//当当前元素为最后一个元素时,下一个元素的访问会越界,所以多给了一个空间
a.resize(n+1);
//范围for,与for(int i=0;i<n;i++)一致,具体可参考博客初始C++
for(auto& e:a)
{
cin>>e;
}
//题目给出的数据都是正整数,所以最够一个位置给0不会影响结果,此处只是为了避免越界访问的问题
a[n]=0;
int count=0;
int i=0;
while(i<n)
{
if(a[i]<a[i+1])
{
//循环到a[i+1]<a[i]的位置
//即对应找到非递减结束的位置
//同时需要保证到i不会超过数组的元素个数
while(a[i]<a[i+1]&&i<n)
{
i++;
}
count++;
i++;
}
if(a[i]==a[i+1])
{
i++;
}
if(a[i]>a[i+1])
{
while(a[i]>a[i+1]&&i<n)
{
i++;
}
count++;
i++;
}
}
cout<<count;
return 0;
}
关于题解代码的解释已经在代码中以注释的形式给出,整体思路几乎没有区别,不过题解的代码更加简洁,也考虑的更加周全,需要注意的是,这道题牛客网并没有检测到访问越界,所以就算设置成n也是没有问题的,不过自己还是需要知道存在这些问题,才能防患于未然。