您的位置:首页 > 其它

单调队列优化DP

2017-04-09 10:44 211 查看
单调队列优化DP:顾名思义,就是拿单调队列对DP进行优化。可以把N维的DP降低到N-1维。

先来看一道例题:

//---------------------------------------------------------------------------------------------------------------//

输入一个长度为n的整数序列,从中找出一段不超过M的连续子序列,使得整个序列的和最大。(n<=300000)

例如 1,-3,5,1,-2,3

当m=4时,S=5+1-2+3=7
当m=2或m=3时,S=5+1=6

//----------------------------------------------------------------------------------------------------------------//

对于这道题,很明显可以用DP来解决,DP方程为:

f[i] = sum[i] - max(sum[j] | i-M<=j<=i)

答案为max(f[i] | 1<=i<=n)。

这样做的话,时间复杂度为O(n^2),超时。

注意到max(sum[j]),这时求静态区间的最大值,可以用RMQ进行优化。

时间复杂度为O(nlogn)=O(300000 * 18)=O(5400000)。对于本题,还可以承受。

可如果n再大一点的话,O(nlogn)的时间是无法接受的。对此,就可以用单调队列来进行优化。

对于每一个i:

1,如果sum[队首]>sum[i],则删除队首。

2,插入i

3,若对尾不在范围内(对于本题,即对尾<i-m),删除对尾。

这样的话:就可以保证队列元素(即i)的单调性和队列优先级(即sum[i])的单调性,证明略。

如果上面还看不懂,那就看程序吧。

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <cmath>
#include <cstdlib>
#include <stack>
#include <queue>
#include <map>
#include <list>
#include <set>
using namespace std;
typedef long long LL;
#define Maxn 300010

int n,m;

list<int> h;

LL sum[Maxn];
LL ans;

int main()
{
scanf("%d%d",&n,&m);

int x;
for(int i=1;i<=n;i++)
{
scanf("%d",&x);
sum[i] = sum[i-1] + (LL)x;
}

ans = max(0LL,sum[1]);//全部是负数的情况
h.push_front(1);
for(int i=1;i<=n;i++)
{
for(;( !h.empty() ) && sum[h.front()] > sum[i]; h.pop_front() );//1
h.push_front(i);//2
for(;( !h.empty() ) && i-m > h.back(); h.pop_back() );//3
ans = max(ans,sum[i] - sum[h.back()]);//计算答案
}
printf("%I64d\n",ans);
return 0;
}


所以,每当遇到形如f[i] = max or min(a[k] | p[i]<=k<=q[i]) + b[i] (q[i],p[i]单调,b[i]与k无关)这样的DP方程的时候,就可以用如上方法进行单调队列优化。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  优化 DP