1.前缀和+单调队列:https://www.acwing.com/problem/content/137/
我们先预处理下前缀和,以下标为i的点为有边界:
也就是求()的min,考虑到j的范围是定值,用单调队列维护即可。
AC代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
struct node{
int t;
ll zhi;
};
int n,m;
ll a[300006];
ll sum[300006];
node q[300006];
int tail=-1,head=0;
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++) sum[i]=sum[i-1]+a[i];
ll ans=-1e18;
for(int i=0;i<=n;i++){
if(tail>=head&&(q[head].t<=i-m-1)) head++;
if(tail>=head) ans=max(ans,sum[i]-q[head].zhi);
while(tail>=head&&q[tail].zhi>=sum[i]) tail--;
q[++tail]={i,sum[i]};
}
cout<<ans;
}
2.DP+单调队列:https://www.acwing.com/problem/content/1089/
先维护出来前缀和,令表示前i个的最大效率,答案就是
我们考虑状态转移:
于是我们把dp值放入单调队列即可。
AC代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n,k;
ll a[100010],sum[100010],dp[100010];
ll q[100010],head=0,tail=-1;
int main(){
cin>>n>>k;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++) sum[i]=sum[i-1]+a[i];
dp[0]=a[0];
q[++tail]=0;
for(int i=1;i<=n;i++){
while(tail>=head&&i-k>q[head]) head++;
dp[i]=max(sum[i]+dp[q[head]-1]-sum[q[head]],dp[i-1]);
ll xx=dp[i-1]-sum[i];
while(tail>=head&&xx>dp[q[tail]-1]-sum[q[tail]]) tail--;
q[++tail]=i;
}
cout<<dp[n];
}
3.DP+单调队列:https://www.acwing.com/problem/content/1091/
令dp[i]表示前i个第i个一定选的最小花费:
,用单调队列维护。
AC代码:
#include<bits/stdc++.h>
using namespace std;
int n,m;
int a[200010],dp[200010];
int q[200010],tail=-1,head=0;
int ans=0x7f7f7f7f;
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>a[i];
q[++tail]=1;
dp[1]=a[1];
for(int i=2;i<=n;i++){
while(tail>=head&&q[head]<i-m) head++;
if(i<=m) dp[i]=a[i];
else dp[i]=a[i]+dp[q[head]];
while(tail>=head&&dp[i]<dp[q[tail]]) tail--;
q[++tail]=i;
}
//for(int i=1;i<=n;i++) cout<<dp[i]<<" ";
for(int i=n-m+1;i<=n;i++) ans=min(ans,dp[i]);
cout<<ans;
}
4.二维的单调队列:https://www.acwing.com/problem/content/1093/
想到要维护出来每一个正方形的max与min,我们可以考虑先对行进行单调队列,求出行方向连续k个的最值,然后再对列进行一遍,这样就求出每一个正方行的最值了。
AC代码:
#include<bits/stdc++.h>
using namespace std;
const int N = 1010;
int n, m, k, res = 1e9;
int w[N][N], minv[N][N], maxv[N][N], que[N];
int a[N], b[N], c[N], d[N];
void get_max(int a[], int f[], int m)
{
int hh = 0, tt = -1;
for (int i = 1; i <= m; i ++ )
{
while (hh <= tt && i - que[hh] >= k) hh ++ ;
while (hh <= tt && a[i] >= a[que[tt]]) tt -- ;
que[ ++ tt] = i;
f[i] = a[que[hh]];
}
}
void get_min(int a[], int f[], int m)
{
int hh = 0, tt = -1;
for (int i = 1; i <= m; i ++ )
{
while (hh <= tt && i - que[hh] >= k) hh ++ ;
while (hh <= tt && a[i] <= a[que[tt]]) tt -- ;
que[ ++ tt] = i;
f[i] = a[que[hh]];
}
}
int main()
{
scanf("%d%d%d", &n, &m, &k);
for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= m; j ++ )
scanf("%d", &w[i][j]);
for (int i = 1; i <= n; i ++ )
{
get_min(w[i], minv[i], m);
get_max(w[i], maxv[i], m);
}
for (int i = k; i <= m; i ++ )
{
for (int j = 1; j <= n; j ++ )
{
a[j] = maxv[j][i];
b[j] = minv[j][i];
}
get_max(a, c, n);
get_min(b, d, n);
for (int j = k; j <= n; j ++ ) res = min(res, c[j] - d[j]);
}
printf("%d\n", res);
}