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

题目:
在一条环形公路旁均匀地分布着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。
如下图
环路运输(环形结构上的动规并单调队列优化)_第1张图片

综上所述,原问题可以等价转化为:长度为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算法进行排序

你可能感兴趣的:(动态规划)