【思维 + 贪心】AcWing 134. 双端队列
这题的思想还是很有意思的~
分析
考虑将读入的数处理成 pair
数组,第一个属性代表读入的值,第二个属性代表下标。
然后将 pair
数组对值升序排序,可以发现,如果想要 pair
连续的一段出现在同一个双端队列中,那么下标一定是先递减再递增(像山谷一样)(当然,单调这种退化的形式也算)。
为什么下标一定是先递减再递增呢?因为值处于中间的元素下标值一定是最小的,而越向两边扩展,其下标一定越大,因此呈山谷一样的形状。
有了这个性质,下面考虑如何统计答案。
因为相同的值之间对应的下标值可以相互调整,我们需要找到最优的调整方案使得山谷出现次数最小。
-
直观地考虑,先将值相同的每一段对应的下标值存起来(具体实现可以用
vector
)。 -
接着从左到右扫一遍,维护一个状态 , 时代表正在递减状态,否则为递增状态。
-
不难发现初始状态设置为 一定是最优的。
最后分类讨论:
- 第 段 时,当第 段的最大值比 段的最小值小,那么我们这一段也贪心地决策为递减(也就是 值保持);否则,我们贪心地将 变为 。
- 第 段 时,当第 段的最小值比 段最大值大,那么我们这一段也决策为递增;否则,我们贪心地将 变为 。
为什么这样贪心是对的呢?
以第一个情况为例,第二个类似。
- 当第 段的最大值比 段的最小值小,我们保持第 段 (决策保持单减)(我们记为决策 ),我们证明这样做一定不比决策变为单增(记为决策 )差。
证明:
第 段如果我们决策为 ,那么决策 的 三段的总代价为 ,而决策 的代价为 。
第 段如果我们决策为 ,那么决策 的 三段的总代价为 ,而决策 的代价为 。
因此无论 段如何决策,决策 都不比 差。
- 当第 段的最大值比 段的最小值大,我们让第 段 (决策改变为单增)(我们记为决策 ),我们证明这样做一定不比决策第 段 (保持单减,记为决策 )差。
证明:
第 段如果我们决策为 ,那么决策 的 三段的总代价为 ,而决策 的代价为 。
第 段如果我们决策为 ,那么决策 的 三段的总代价为 ,而决策 的代价为 。
因此无论 段如何决策,决策 都不比 差。
至此,证明完毕。
实现
// Problem: 双端队列
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/136/
// Memory Limit: 64 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include<bits/stdc++.h>
using namespace std;
#define debug(x) cerr << #x << ": " << (x) << endl
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define dwn(i,a,b) for(int i=(a);i>=(b);i--)
#define pb push_back
#define all(x) (x).begin(), (x).end()
using pii = pair<int, int>;
using ll = long long;
inline void read(int &x){
int s=0; x=1;
char ch=getchar();
while(ch<'0' || ch>'9') {if(ch=='-')x=-1;ch=getchar();}
while(ch>='0' && ch<='9') s=(s<<3)+(s<<1)+ch-'0',ch=getchar();
x*=s;
}
#define x first
#define y second
const int N=2e5+5;
int n;
pii e[N];
int main(){
cin>>n;
rep(i,1,n) read(e[i].x), e[i].y=i;
sort(e+1, e+1+n);
vector<vector<int>> blk;
vector<int> rec;
rec.pb(e[1].y);
rep(i,2,n){
if(e[i].x!=e[i-1].x){
blk.pb(rec);
rec.clear();
}
rec.pb(e[i].y);
}
if(rec.size()) blk.pb(rec);
if(blk.size()==1) return puts("1"), 0;
bool ok=1;
int res=1;
rep(i,1,blk.size()-1){
if(ok){
if(*min_element(all(blk[i-1]))<*max_element(all(blk[i]))){
ok=0;
}
}
else{
if(*max_element(all(blk[i-1]))>*min_element(all(blk[i]))){
ok=1;
res++;
}
}
}
cout<<res<<endl;
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】