单调队列优化dp
单调队列可以有两个操作:
1、插入一个新的元素,该元素从队尾开始向队首进行搜索,找到合适的位置插入之,如果该位置原本有元素,则替换它。
2、在过程中从队首删除不符合当前要求的元素。
单调队列实现起来可简单,可复杂。简单的一个数组,一个head,一个tail指针就搞定。复杂的用双向链表实现。
用处:
1、保存最优解,次优解,ect。
2、利用单调队列对dp方程进行优化,可将O(n)复杂度降至O(1)。也就是说,将原本会超时的N维dp降优化至N-1维,以求通过。这也是我想记录的重点
是不是任何DP都可以利用单调队列进行优化呢?答案是否定的。
记住!只有形如 dp[i]=max/min (f[k]) + g[i] (k<i && g[i]是与k无关的变量)才能用到单调队列进行优化。(降维实质:把状态转移方程分为两部分:只与k有关,只与i有关。 与k有关的状态在前i-1次压入que,i有关的是此次状态转移所需的g[i])
优化的对象就是f[k]。
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
long long mp[105][10005];
long long dp[105][10005];
long long val[10005];
int que[10005];
long long inf=-1e18;
int main()
{
//cout<<inf<<endl;
int n,m,x,t;
while(~scanf("%d%d%d%d",&n,&m,&x,&t))
{
for(int i=1; i<=n; i++)
{
for(int j=1; j<=m; j++)
{
scanf("%lld",&mp[i][j]);
dp[i][j]=inf;
}
}
dp[1][x]=mp[1][x];
long long sum=0;
for(int i=1; i<=t; i++)
{
int cur=x+i;
if((cur)>m)
break;
sum+=mp[1][cur];
dp[1][cur]=sum+dp[1][x];
}
sum=0;
for(int i=1; i<=t; i++)
{
int cur=x-i;
if((cur)<1)
break;
sum+=mp[1][cur];
dp[1][cur]=sum+dp[1][x];
}
for(int i=2; i<=n; i++)
{
dp[i][1]=max(dp[i][1],dp[i-1][1]+mp[i][1]);
long long tsum=0;
val[1]=dp[i-1][1];
int head=1,tail=0;
que[++tail]=1;
tsum+=mp[i][1];
for(int j=2; j<=m; j++)
{
//cout<<j<<": "<<que[tail]<<endl;
long long tm=val[que[head]]+tsum;
tm=max(tm,dp[i-1][j]);
dp[i][j]=max(dp[i][j],tm+mp[i][j]);
val[j]=dp[i-1][j]-tsum;
tsum+=mp[i][j];
while(head <= tail && val[que[tail]] <= val[j])
tail--;
que[++tail] = j;
while(head <= tail && j - que[head] >= t)
head++;
}
dp[i][m]=max(dp[i][m],dp[i-1][m]+mp[i][m]);
tsum=0;
val[m]=dp[i-1][m];
head=1,tail=0;
que[++tail]=m;
tsum+=mp[i][m];
for(int j=m-1; j>=1; j--)
{
long long tm=val[que[head]]+tsum;
tm=max(tm,dp[i-1][j]);
dp[i][j]=max(dp[i][j],tm+mp[i][j]);
val[j]=dp[i-1][j]-tsum;
tsum+=mp[i][j];
while(head <= tail && val[que[tail]] <= val[j])
tail--;
que[++tail] = j;
while(head <= tail && que[head]-j >= t)
head++;
}
}
long long ans=inf;
for(int i=1; i<=m; i++)
ans=max(ans,dp[n][i]);
printf("%lld\n",ans);
/* for(int i=1; i<=n; i++)
{
for(int j=1; j<=m; j++)
cout<<dp[i][j]<<" ";
cout<<endl;
}
*/
}
}
/*
for(int j=1;j<=m;j++)
cout<<dp[i][j]<<" ";
cout<<endl;
3 4 2 2
-12 -3 23 -5
-2 -2 -2 -4
2 1 -3 7
3 5 3 5
1 1 1 0 0
1 1 1 1 1
0 1 1 1 1
*/