一、单调队列
单调队列,即单调递减或单调递增的队列。使用频率不高,但在有些程序中会有非同寻常的作用。——百度百科
重点在哪里?
使用频率不高 单调递减或单调递增的队列。
单调队列是一个双端队列,一般来说队尾用于维护单调性,队头用于保持队列大小。
单调队列有两个性质:
- 队列中的元素在原来列表中的顺序是单调递增的。
- 队列中的元素大小是单调递增或递减。
二、实现
我们将单调队列看做是一个大小一定的窗口,在列表中滑动,维护以上两个性质。
我们将单调队列的维护分为两步:
-
维护队尾。比较列表当中的当前元素与队尾元素,若当前元素更优,则弹出队尾,直到插入当前元素可以满足单调性时,将当前元素插入队尾。
-
维护队头。将数组下标在窗口之外的弹出。
#include<iostream>
#include<cstdio>
#define MAXN 1000010
using namespace std;
int n,k;
int a[MAXN];
int q[MAXN],p[MAXN],head,tail;
void get_min()
{
head=1;
tail=0;
for(int i=1;i<=n;i++)
{
while(tail>=head&&q[tail]>a[i])
tail--;
q[++tail]=a[i];
p[tail]=i;
while(p[head]<=i-k)
head++;
if(i>=k)
printf("%d ",q[head]);
}
printf("\n");
}
void get_max()
{
head=1;
tail=0;
for(int i=1;i<=n;i++)
{
while(tail>=head&&q[tail]<a[i])
tail--;
q[++tail]=a[i];
p[tail]=i;
while(p[head]<=i-k)
head++;
if(i>=k)
printf("%d ",q[head]);
}
printf("\n");
}
int main()
{
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
get_min();
get_max();
return 0;
}
三、应用:优化DP
因为单调队列的单调性,所以我们可以用它来进行降低DP维数,进行时间和空间的优化。
例题:粉刷木板
题目描述
有 N N N 块木板从左到右排成一行,有 M M M 个工匠对这些木板进行粉刷,每块木板至多被粉刷一次。
第 i i i 个木匠要么不粉刷,要么粉刷包含木板 S i S_i Si 且长度不超过 L i L_i Li 的连续的一段木板,每粉刷一块可以得到 P i P_i Pi 的报酬。不同工匠的 $ i _i i不同。 请问如何安排能使工匠们获得的总报酬最多。
输入格式
第一行包含两个整数 N N N 和 M M M 。
接下来 M M M 行,每行包含三个整数 L i , P i , S i L_i,P_i,S_i Li,Pi,Si。
输出格式
输出一个整数,表示结果。
样例输入
8 4
3 2 2
3 2 3
3 3 5
1 1 7
样例输出
17
数据范围与提示
对于 100 % 100\% 100%的数据, 1 ≤ N ≤ 16000 1≤N≤16000 1≤N≤16000, 1 ≤ M ≤ 100 1≤M≤100 1≤M≤100, 1 ≤ P i ≤ 10000 1≤P_i≤10000 1≤Pi≤10000。
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int n,m;
struct node
{
int l,p,s;
}
a[110];
bool cmp(node a,node b)
{
return a.s<b.s;
}
int q[20000],head,tail;
int f[110][20000];
void dp()
{
for(int i=1;i<=m;i++)
{
head=1,tail=0;
for(int k=max(a[i].s-a[i].l,0);k<=a[i].s-1;k++)
{
while(head<=tail&&f[i-1][q[tail]]-q[tail]*a[i].p<=f[i-1][k]-k*a[i].p)
tail--;
q[++tail]=k;
}
for(int j=1;j<=n;j++)
{
f[i][j]=max(f[i-1][j],f[i][j-1]);
if(j>=a[i].s)
{
while(head<=tail&&j-q[head]>a[i].l)
head++;
if(head<=tail)
f[i][j]=max(f[i][j],f[i-1][q[head]]+(j-q[head])*a[i].p);
}
}
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
scanf("%d%d%d",&a[i].l,&a[i].p,&a[i].s);
sort(a+1,a+m+1,cmp);
dp();
printf("%d",f[m][n]);
return 0;
}