考试考了两遍单调队列 第一遍不懂,第二遍没满分(郁闷)
单调队列可以用来优化dp,但是我这次只学了区间和的优化(毕竟叫笔记 dp的坑就不填了)(其实是懒)
因为单调队列非常简单(虽然我就是看不懂)
所以我这里就放三个题的代码供思考(基本囊括所有的板子)
1:最大字段和
描述
输入一个长度为n的整数序列,从中找出一段不超过m的连续子序列,使得整个序列的和最大。
例如 1,-3,5,1,-2,3
当m=4时,S=5+1-2+3=7
当m=2或m=3时,S=5+1=6
输入格式
第一行两个数n,m(n,m<=300000)
第二行有n个数,要求在n个数找到最大子序和
输出格式
一个数,数出他们的最大子序和
样例输入
6 4
1 -3 5 1 -2 3
样例输出
7
代码
//#define fre yes
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 300005;
int q[maxn];
int sum[maxn];
int n,m,ans=-1e9;
template<typename T>inline void read(T&x)
{
x = 0;char c;int lenp = 1;
do { c = getchar();if(c == '-') lenp = -1; } while(!isdigit(c));
do { x = x * 10 + c - '0';c = getchar(); } while(isdigit(c));
x *= lenp;
}
int main()
{
read(n);read(m);
for (int i=1;i<=n;i++)
{
int x;
read(x);
sum[i] = sum[i-1] + x;
}
int l = 1,r = 1;
q[1] = 0;
for (int i=1;i<=n;i++)
{
while(l<=r && q[l] < i - m) l++;
ans = max(ans,sum[i] - sum[q[l]]);
while(l<=r && sum[q[r]] >= sum[i]) r--;
q[++r] = i;
} printf("%d
",ans);
return 0;
}
2:扫描
题目描述
有一个 1 ∗ n 的矩阵,有 n 个正整数。
现在给你一个可以盖住连续的 k 的数的木板。
一开始木板盖住了矩阵的第 1 ∼ k 个数,每次将木板向右移动一个单位,直到右端与
第 n 个数重合。
每次移动前输出被覆盖住的最大的数是多少。
输入格式:
从 scan.in 中输入数据
第一行两个数,n,k,表示共有 n 个数,木板可以盖住 k 个数。
第二行 n 个数,表示矩阵中的元素。
输出格式:
输出到 scan.out 中
共 n − k + 1 行,每行一个正整数。
第 i 行表示第 i ∼ i + k − 1 个数中最大值是多少。
输入样例#1:
5 3
1 5 3 4 2
输出样例#1:
5
5
4
说明
对于 20% 的数据保证:1 ≤ n ≤ 1e3,1 ≤ k ≤ n
对于 50% 的数据保证:1 ≤ n ≤ 1e4,1 ≤ k ≤ n
对于 100% 的数据保证:1 ≤ n ≤ 2 ∗ 1e6,1 ≤ k ≤ n
矩阵中元素大小不超过 1e4。
代码
//#define fre yes
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 1000005;
int a[maxn];
int q[maxn];
int ans[maxn];
int n,K;
template<typename T>inline void read(T&x)
{
x = 0;char c;int lenp = 1;
do { c = getchar();if(c == '-') lenp = -1; } while(!isdigit(c));
do { x = x * 10 + c - '0';c = getchar(); } while(isdigit(c));
x *= lenp;
}
int main()
{
read(n);read(K);
for(int i=1;i<=n;i++) read(a[i]);
int l=1,r = 0;
for(int i=1;i<=n;i++)
{
while(l<=r&&q[l]<=i-K)l++;
while(l<=r&&a[i]>a[q[r]])r--;
q[++r]=i;
ans[i]=a[q[l]];
}
for(int i=K;i<=n;i++)printf("%d
",ans[i]);
return 0;
}
**3.滑动的窗户** ####题目描述 在一个包含 n 个元素的数组上,有一个长度为 k 的窗户在从左向右滑动。窗户每滑动到一个位置,我们都可以看到 k 个元素在窗户中。如下的例子所示,假设数组为 [1 3 -1 -3 5 3 6 7],而 k 等于 3 :
窗户位置 最小值 最大值
[1 3 -1] -3 5 3 6 7 -1 3
1 [3 -1 -3] 5 3 6 7 -3 3
1 3 [-1 -3 5] 3 6 7 -3 5
1 3 -1 [-3 5 3] 6 7 -3 5
1 3 -1 -3 [5 3 6] 7 3 6
1 3 -1 -3 5 [3 6 7] 3 7
对于窗户滑动过的每个位置,请给出窗户内 k 个元素的最小值和最大值。
输入格式:
输入的第一行包括两个整数 n,k ,n 表示数组的长度,k 表示窗户的长度。
接下来一行包括 n 个整数,表示这个 n 个元素的数组。
输出格式:
输出包含两行,每行包括 n-k+1 个整数。
第一行表示窗户从左到右滑动过程中的最小值。
第二行表示窗户从左到右滑动过程中的最大值。
样例输入:
8 3
1 3 -1 -3 5 3 6 7
样例输出:
-1 -3 -3 -3 3 3
3 3 5 5 6 7
数据范围:
对于 100% 的数据,3<=n<=1000000,1<=k<=n,数组中的每个元素均在 int 范围内。
附代码:
//#define fre yes
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 1000010;
int arr[maxn];
int summax[maxn],p[maxn],hub[maxn],h=1,t=0;
int summin[maxn],q[maxn],num[maxn],l=1,r=0;
int n,m;
template<typename T>inline void read(T&x)
{
x = 0;char c;int lenp = 1;
do { c = getchar();if(c == '-') lenp = -1; } while(!isdigit(c));
do { x = x * 10 + c - '0';c = getchar(); } while(isdigit(c));
x *= lenp;
}
int main()
{
read(n);read(m);
for(int i=1;i<=n;i++)
{
read(arr[i]);
while(num[l]<=i-m) l++;
while(q[r]>=arr[i]&&r>=l) r--;
q[++r]=arr[i];
num[r]=i;
summin[i]=q[l];
while(hub[h]<=i-m) h++;
while(p[t]<=arr[i]&&t>=h) t--;
p[++t]=arr[i];
hub[t]=i;
summax[i]=p[h];
}
for(int i=m;i<=n;i++) printf("%d ",summin[i]); puts("");
for(int i=m;i<=n;i++) printf("%d ",summax[i]); puts("");
return 0;
}