环路运输(环形结构上的动规并单调队列优化)

题目:
在一条环形公路旁均匀地分布着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<i≤N),如果i-j≤N/2(1≤i≤N),那么N-(i-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<=2N并且i-j≤N/2的哪两座仓库i和j之间运送货物,运送代价Ai+Aj+i-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<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
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<bits/stdc++.h>
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算法进行排序

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值