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