题目:
在一条环形公路旁均匀地分布着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)。求在哪两座仓库之间运送货物需要的代价最大。1≤N≤106, 1<=Ai<=107 。
输入
第一行一个整数N,第二行N个整数A1~AN。
输出
一个整数,表示最大代价
样例输入
5
1 8 6 2 5
样例输出
15
算法思想:
我们在任意位置(例如仓库1和N之间)把环断开,复制一倍接在末尾,形成长度为2N的直线公路。在转化之后的模型中,公路旁均匀分布着2N座仓库,其中Ai=Ai+N(1<=i<=N)。
对于原来环形公路上的任意两座仓库i和j(1≤j=N/2,根据距离公式dist(i,j)=min(|i-j|,N-|i-j|), 我们选i-j为你两个仓库之间的距离。那么在新的直线公路上,和原来没有区别,i和j之间运送货物,代价仍然为Ai+Aj+i-j。
同理:
如果i-j>N/2,那么可以对应成在新直线公路上 i 和 j+N 之间运送货物,代价为Ai+Aj+N+j+N-i,其中j+N-i=N-(i-j)≤N/2。
如下图
综上所述,原问题可以等价转化为:长度为2N的直线公路上,在满足1≤j
由i-j≤N/2,移项的i-N/2≤ j , 且 j>=i-N/2,所以枚举j的范围为[i-N/2,i-1]。
我们可以枚举i,对于每个i,需要在[i-N/2,i-1]范围枚举j,Ai+Aj+i-j式子转换为Ai+i +(Aj-j),对于外层循环每个i,Ai+i为定值,枚举 j 使Aj-j尽量大。
用单调队列进行维护Aj-j,可以在均摊O(1)的时间内找到这样的 j 。
整个算法的时间复杂度为O(N)。
#include
#include
#include
#include
using namespace std;
int a[2000010], q[2000010], n, ans;
int main() {
cin >> n;
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
a[n + i] = a[i];
}
int l = 1, r = 1;
q[1] = 1;
for (int i = 2; i <= 2 * n; i++) {
while (l <= r && q[l] < i - n / 2) l++;
ans = max(ans, a[i] + a[q[l]] + i - q[l]);
while (l < r && a[i] - i >= a[q[r]] - q[r]) r--;
q[++r] = i;
}
cout << ans << endl;
}
#include
using namespace std;
const int N = 2e6 + 10;
int a[N], n, ans;
deque<int> q;//STL双端队列
int main() {
cin >> n;
int len = n >> 1;
for (int i = 1; i <= n; i++)
scanf("%d", &a[i]), a[i + n] = a[i];
q.push_back(1);
for (int i = 2; i < n + len; i++) {
while (i - q.back() > len)q.pop_back();
ans = max(ans, a[i] + a[q.back()] + i - q.back());
while (!q.empty() && a[q.front()] - q.front() <= a[i] - i)q.pop_front();
q.push_front(i);
}
cout << ans << endl;
return 0;
}
补充内容:
在使用前要添加 #include<deque>头文件
deque构造函数
deque deq; //默认构造形式
deque(beg,end); //将[beg,end]区间的元素拷贝到本身
deque(n,elem); //将n个elem元素拷贝到本身
deque(const deque &deq); //将对象deq拷贝到本身
deque赋值操作
有两种方法:操作符 = 和 .assign( )
assign(beg,end); //将[beg,end]区间的元素拷贝到本身
assign(n,elem); //将n个elem元素拷贝到本身
deque大小操作
deque.empty(); //判断是否为空
deque.size(); //返回容器中元素的个数
deque.resize(int num); //将容器的容量从新设置为num,若容量变大,则以默认值填充新位置;若容量变小,则超出容器容量的元素被删除
deque.resize(int num,elem); //将容器的容量从新设置为num,若容量变大,则以元素elem填充新位置;若容量变小,则超出容器容量的元素被删除
deque插入和删除
两端插入操作:
deque.push_back(elem); //在容器尾部添加元素elem
deque.push_front(elem); //在容器头部添加元素elem
deque.pop_back(); //删除容器尾部元素
deque.pop_front(); //删除容器头部元素
指定位置操作:
deque.insert(pos,elem); //在迭代器pos位置插入元素elem
deque.insert(pos,n,elem); //在迭代器pos位置插入n个元素elem
deque.insert(pos,beg,end); //在迭代器pos位置插入区间[deque.begin(),deque.end()]内的元素
deque.clear(); //清空容器内所有数据
deque.erase(beg,end); //清除区间[deque.begin(),deque.end()]内的元素
deque.erase(pos); //清除迭代器pos位置的元素
deque数据存取
deque.at(int idx); //返回索引idx所指的数据
deque[int idx]; //返回索引idx所指的数据
deque.front(); //返回容器第一个数据元素
deque.back(); //返回容器最后一个数据元素
deque排序
在使用前要添加 #include<algorithm>头文件
sort(dque.begin(),deque.end()); //对begin和end区间元素进行排序
对于支持随机访问的迭代器都可以利用sort算法进行排序