目录
1.滑动窗口
2.最大子序和
3.旅行问题
4.烽火传递
5.绿色通道
6.修剪草坪
7.理想的正方形
1.滑动窗口
154.给定一个大小为 n≤106 的数组。
有一个大小为 k 的滑动窗口,它从数组的最左边移动到最右边。
你只能在窗口中看到 k 个数字。
每次滑动窗口向右移动一个位置。
以下是一个例子:
该数组为 [1 3 -1 -3 5 3 6 7]
,k 为 33。
窗口位置 | 最小值 | 最大值 |
---|---|---|
[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 |
你的任务是确定滑动窗口位于每个位置时,窗口中的最大值和最小值。
输入格式
输入包含两行。
第一行包含两个整数 n 和 k,分别代表数组长度和滑动窗口的长度。
第二行有 n 个整数,代表数组的具体数值。
同行数据之间用空格隔开。
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1000010;
int n, k;
int a[N], q[N];
int main()
{
scanf("%d%d", &n, &k);
for(int i = 0;i < n; i++)
scanf("%d", &a[i]);
int tt = -1, hh = 0;
for(int i = 0;i < n; i++)
{
if(hh <= tt && q[hh] < i - k + 1) hh++;
while(hh <= tt && a[q[tt]] >= a[i]) tt--;
q[++tt] = i;
if(i >= k - 1) printf("%d ", a[q[hh]]);
}
puts("");
tt = -1, hh = 0;
for(int i = 0;i < n; i++)
{
if(hh <= tt && q[hh] < i - k + 1) hh++;
while(hh <= tt && a[q[tt]] <= a[i]) tt--;
q[++tt] = i;
if(i >= k - 1) printf("%d ", a[q[hh]]);
}
puts("");
return 0;
}
2.最大子序和
输入一个长度为 n 的整数序列,从中找出一段长度不超过 m 的连续子序列,使得子序列中所有数的和最大。
注意: 子序列的长度至少是 1。
输入格式
第一行输入两个整数 n,m。
第二行输入 n 个数,代表长度为 n 的整数序列。
同一行数之间用空格隔开。
闫氏最优化问题分析法
在一个有限集合中求最值,或者求个数。
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 300010;
int n, m;
int s[N], q[N];
int main()
{
scanf("%d%d", &n, &m);
for(int i = 1;i <= n; i++)
{
scanf("%d", &s[i]);
s[i] += s[i - 1];
}
int res = 0;
for(int i = 1;i <= n; i++)
{
res = min(res, s[i] - s[i - 1]);
}
int hh = 0, tt = 0;
for(int i = 1;i <= n; i++)
{
if(q[hh] < i - m) hh++;
res = max(res, s[i] - s[q[hh]]);
while(hh <= tt && s[q[tt]] >= s[i]) tt--;
q[++tt] = i;
}
printf("%d\n", res);
return 0;
}
3.旅行问题
1088.John 打算驾驶一辆汽车周游一个环形公路。
公路上总共有 n 个车站,每站都有若干升汽油(有的站可能油量为零),每升油可以让汽车行驶一千米。
John 必须从某个车站出发,一直按顺时针(或逆时针)方向走遍所有的车站,并回到起点。
在一开始的时候,汽车内油量为零,John 每到一个车站就把该站所有的油都带上(起点站亦是如此),行驶过程中不能出现没有油的情况。
任务:判断以每个车站为起点能否按条件成功周游一周。
输入格式 第一行是一个整数 n,表示环形公路上的车站数;
接下来 n 行,每行两个整数 pi,di,分别表示表示第 i 号车站的存油量和第 i 号车站到 顺时针方向 下一站的距离。
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 1e6 + 10;
typedef long long LL;
int n;
int p[N], d[N];
LL s[N];
int q[N];
bool st[N];
int main()
{
scanf("%d", &n);
for(int i = 1;i <= n; i++)
scanf("%d%d", &o[i], &d[i]);
//顺时针链
for(int i = 1;i <= n; i++)
s[i] = s[i + 1] = o[i] - d[i];
for(int i = 1;i <= n * 2; i++)
s[i] += s[i - 1];
int hh = 0, tt = -1;
for(int i = n * 2;i >= 1; i--)
{
if(hh <= tt && q[hh] > i + n) hh++;
while(hh <= tt && s[q[tt]] >= s[i]) tt--;
q[++tt] = i;
if(i <= n)
{
if(s[q[hh]] >= s[i - 1])
st[i] = true;
}
}
//逆时针链
d[0] = d[n];
for(int i = 1;i <= n; i++)
s[i] = s[i + 1] = o[i] - d[i - 1];
for(int i = 1;i <= 2 * n; i++)
s[i] += s[i - 1];
hh = 0, tt = -1;
for(int i = 1;i <= 2 * n; i++)
{
if(hh <= tt && q[hh] < i - n) hh++;
if(i > n)
{
if(s[q[hh]] <= s[i])
st[i - n] = true;
}
while(hh <= tt && s[q[tt]] <= s[i]) tt--;
q[++tt] = i;
}
for(int i = 1;i <= n; i++)
if(st[i]) puts("TAK");
else puts("NIE");
return 0;
}
4.烽火传递
烽火台是重要的军事防御设施,一般建在交通要道或险要处。
一旦有军情发生,则白天用浓烟,晚上有火光传递军情。
在某两个城市之间有 n 座烽火台,每个烽火台发出信号都有一定的代价。
为了使情报准确传递,在连续 m 个烽火台中至少要有一个发出信号。
现在输入 n,m 和每个烽火台的代价,请计算在两城市之间准确传递情报所需花费的总代价最少为多少。
输入格式 第一行是两个整数 n,m,具体含义见题目描述;
第二行 n 个整数表示每个烽火台的代价 ai。
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 200010;
int n, m;
int w[N];
int f[N];
int q[N];
int main()
{
scanf("%d%d", &n, &m);
for(int i = 1;i <= n; i++) scanf("%d", &w[i]);
int hh = 0, tt = 0;
for(int i = 1;i <= n; i++)
{
if(q[hh] < i - m) hh++;
f[i] = f[q[hh]] + w[i];
while(hh <= tt && f[q[tt]] >= f[i]) tt--;
q[++tt] = i;
}
int res = 1e9;
for(int i = n - m + 1;i <= n; i++)
res = min(res, f[i]);
return 0;
}
详细学习:模拟队列
5.绿色通道
高二数学《绿色通道》总共有 n 道题目要抄,编号 1,2,…,n,抄第 i 题要花 ai 分钟。
小 Y 决定只用不超过 t 分钟抄这个,因此必然有空着的题。
每道题要么不写,要么抄完,不能写一半。
下标连续的一些空题称为一个空题段,它的长度就是所包含的题目数。
这样应付自然会引起马老师的愤怒,最长的空题段越长,马老师越生气。
现在,小 Y 想知道他在这 t 分钟内写哪些题,才能够尽量减轻马老师的怒火。
由于小 Y 很聪明,你只要告诉他最长的空题段至少有多长就可以了,不需输出方案。
输入格式
第一行为两个整数 n,t。
第二行为 n 个整数,依次为 a1,a2,…,an。
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 50010;
int n, m;
int q[N], f[N];
int w[N];
bool check(int limit)
{
int hh = 0, tt = 0;
for(int i = 1;i <= n; i++)
{
if(q[hh] < i - limit - 1) hh++;
f[i] = f[q[hh]] + w[i];
while(hh <= tt && f[q[tt]] >= f[i]) tt--;
q[++tt] = i;
}
for(int i = n - limit;i <= n; i++)
if(f[i] <= m)
return true;
return false;
}
int main()
{
scanf("%d%d", &n, &m);
for(int i = 1;i <= n; i++)
scanf("%d", &w[i]);
int l = 0, r = n;
while(l < r)
{
int mid = l + r >> 1;
if(check(mid)) r = mid;
else l = mid + 1;
}
printf("%d\n", r);
return 0;
}
6.修剪草坪
1087.在一年前赢得了小镇的最佳草坪比赛后,FJ变得很懒,再也没有修剪过草坪。 现在,新一轮的最佳草坪比赛又开始了,FJ希望能够再次夺冠。 然而,FJ的草坪非常脏乱,因此,FJ只能够让他的奶牛来完成这项工作。 FJ有N只排成一排的奶牛,编号为1到N。 每只奶牛的效率是不同的,奶牛i的效率为。 编号相邻的奶牛们很熟悉,如果FJ安排超过K只编号连续的奶牛,那么这些奶牛就会罢工去开派对。 因此,现在FJ需要你的帮助,找到最合理的安排方案并计算FJ可以得到的最大效率。 注意,方案需满足不能包含超过K只编号连续的奶牛。
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long LL;
const int N = 100010;
int n, m;
LL s[N];
LL f[N];
int q[N];
LL g(int i)
{
return f[i - 1] - s[i];
}
int main()
{
scanf("%d%d", &n, &m);
for(int i = 1;i <= n; i++)
{
scanf("%lld", &s[i]);
s[i] += s[i - 1];
}
int hh = 0, tt = 0;
for(int i = 1;i <= n; i++)
{
if(q[hh] < i - m) hh++;
f[i] = max(f[i - 1], g(q[hh]) + s[i]);
while(hh <= tt && g(q[tt]) <= g[i]) tt--;
q[++tt] = i;
}
printf("%lld\n", f[n]);
return 0;
}
440.道路问题(非常复杂)
7.理想的正方形
有一个a x b的整数组成的矩阵,现请你从中找出一个n x n的正方形区域,使得该区域所有数中的最大值和最小值之差最小。
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 1010;
int n, m, k;
int w[N][N];
int row_max[N][N], col_max[N][N];
void get_min(int a[], int b[], int tot)
{
int hh = 0, tt = -1;
for(int i = 1;i <= tot; i++)
{
if(hh <= tt && q[hh] <= i - k) hh++;
while(hh <= tt && a[q[tt]] >= a[i]) tt--;
q[++tt] = i;
b[i] = a[q[hh]];
}
}
void get_max(int a[], int b[], int tot)
{
int hh = 0, tt = -1;
for(int i = 1;i <= tot; i++)
{
if(hh <= tt && q[hh] <= i - k) hh++;
while(hh <= tt && a[q[tt]] <= a[i]) tt--;
q[++tt] = i;
b[i] = a[q[hh]];
}
}
int main()
{
scanf("%d%d", &n, &m);
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], row_min[i], m);
get_max(w[i], row_max[i], m);
}
int res = 1e9;
int a[N], b[N], c[N];
for(int i = k;i <= m; i++)
{
for(int j = 1;j <= n; j++) a[j] = row_min[j][i];
get_min(a, b, n);
for(int j = 1;j <= n; j++) a[j] = row_max[j][i];
get_max(a, c, n);
for(int j = k;j <= n; j++) res -= min(res, c[j] - b[j]);
}
printf("%d\n", res);
return 0;
}