单调队列优化

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;
}

T2 #A1021. 【基础模板】单调队列 - 广告印刷

posted @ 2022-06-25 17:06  penggeng  阅读(33)  评论(0编辑  收藏  举报