在一条环形公路旁均匀地分布着 N 座仓库,编号为 1∼N,编号为 i 的仓库与编号为 j 的仓库之间的距离定义为 dist(i,j)=min(|i−j|,N−|i−j|),也就是逆时针或顺时针从 i 到 j 中较近的一种。
每座仓库都存有货物,其中编号为 i 的仓库库存量为 Ai。
在 i 和 j 两座仓库之间运送货物需要的代价为 Ai+Aj+dist(i,j)。
求在哪两座仓库之间运送货物需要的代价最大。
输入格式
第一行包含一个整数 N。
第二行包含 N 个整数 A1∼AN。
输出格式
输出一个整数,表示最大代价。
数据范围
2≤N≤106,
1≤Ai≤107
输入样例:
5
1 8 6 2 5
输出样例:
15
解析:单调队列,动规
如果使用暴力的方法我们可以使用两个循环,一个枚举i,另一个枚举j
仔细观察这道题,我们可以发现一下性质:
dist(i,j)=min(|i−j|,N−|i−j|)在 | i - j |<n/2 情况下,dist(i,j)=i-j;同时代价 Ai+Aj+dist(i,j) 可以转化成 Ai+i+Aj-j。
所以我们只需要枚举 i ,然后求出 i 右边 len=n/2 的范围内的最大的 Aj-j 即可
到这里可以看出来这可以单调队列来解决
cpp
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<utility>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
using namespace std;
typedef long long LL;
const int N = 2e6 + 5;
int n, m;
int w[N], q[N];
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%d", &w[i]);
w[i + n] = w[i];
}
int hh = 0, tt = -1;
int len = n / 2;
int ret = 0;
for (int i = 1; i <= 2 * n; i++) {
if (hh <= tt && i - q[hh] > len)hh++;
ret = max(ret, w[i] + w[q[hh]] + i - q[hh]);
while (hh <= tt &&w[q[tt]]- q[tt] <= w[i] - i)tt--;//这一步让队列从左往右变得单调,且左边的值最大
q[++tt] = i;
}
printf("%d\n", ret);
return 0;
}