单调队列优化
dp 单调队列优化
T1 P6033 [NOIP2004 提高组] 合并果子 加强版
题目描述:
给定 \(n\) 个数,每次可以将两个数合并并获得为两数和的分数,求合并到只剩一个数时的最小总分数.
\(1 \le n \le 10^7 \quad 1 \le a_i \le 10^5\)
思路:
新知识1 : 双端队列 :
deque<int>q;
速度更快,支持双端操作,支持使用类似于数组的方式 \((q[k])\) 访问.
常用函数:
//入队
q.push_back(k);
q.push_front(k);
q.emplace_back(k);//速度更快,仅限C++14使用
q.emplace_front(k);//速度更快,仅限C++14使用
//出队
q.pop_front();
q.pop_back();
//调用
q.front();
q.back();
q[k];
//数据
q.empty();
q.size();
新知识2 : 双端队列的迭代器----读取队列中的所有数据 :
//读取队列中的所有数据
for(auto i:q) cout<<i<<' '; //Way1
for(auto i=q.begin();i!=q.end();++i) cout<<*i<<' '; //Way2
//均仅限C++14使用
做法:
考虑到 \(a_i\) 较小,先把数据放到桶中,然后按序放到队列 \(q1\) 里, \(q1\) 便成为了一个单调队列.
另外设一个队列 \(q2\) ,每次取 \(q1,q2\) 中最小的两个数进行合并,将合并后的数放入 \(q2\) ,随着所取的数的增大,放入的数也随之增大,于是可证 \(q2\) 也是一个单调队列.
合并到 \(q2\) 中仅剩下一个数时合并结束,共合并 \(n-1\) 次.
Code:
#include<bits/stdc++.h>
#define int long long
#define fr(i,r) for(int i=1;i<=r;++i)
#define For(i,l,r) for(int i=l;i<=r;++i)
#define Rof(i,r,l) for(int i=r;i>=l;--i)
#define pb emplace_back
#define pf pop_front
#define pii pair<int,int>
#define mp(x,y) make_pair(x,y)
#define msec 800
using namespace std;
const int N=1e7+10,A=1e5,Inf=0x7fffffff;
char cch;
int res,zf;
inline int rd()
{
while((cch=getchar())<45);
if(cch^45)res=cch^48,zf=1;
else res=0,zf=-1;
while((cch=getchar())>=48)res=(res*10)+(cch^48);
return res*zf;
}
int buc[A+5];
deque<int>q1,q2;
int n=rd();
int ans;
int m1,m2;
signed main()
{
fr(i,n)++buc[rd()];
fr(i,A)while(buc[i])--buc[i],q1.pb(i);
while(--n)
{
if(!q1.empty()&&!q2.empty())
{
if(q1.front()<q2.front()) m1=q1.front(),q1.pf();
else m1=q2.front(),q2.pf();
if(!q1.empty()&&!q2.empty())
{
if(q1.front()<q2.front()) m2=q1.front(),q1.pf();
else m2=q2.front(),q2.pf();
}
else if(!q2.empty()) m2=q2.front(),q2.pf();
else m2=q1.front(),q1.pf();
}
else if(!q2.empty()) m1=q2.front(),q2.pf(),m2=q2.front(),q2.pf();
else m1=q1.front(),q1.pf(),m2=q1.front(),q1.pf();
q2.pb(m1+m2);
ans+=m1+m2;
//for(auto i:q1)cout<<i<<' ';cout<<'\n';for(auto i:q2)cout<<i<<' ';cout<<"\n\n";
}
cout<<ans;
return 0;
}