Blocking Elements
题目描述
给定一个长度为 \(n\) 的序列 \(A\),你需要划分这个序列。先任意选择若干个位置,假定你选择了 \(m\) 个位置,这些位置分别为 \(B_1,B_2...B_m\) ,这一次划分的代价为下面两个量中的最大值:
- \(\sum\limits_{i=1}^{m}A_{B_{i}}\) .
- \(\max\limits_{i=0}^{m}{\large{\sum\limits_{j=B_i+1}^{B_{i+1}-1}}{A_j}}\) 。 特别的,定义\(B_0=0,B_{m+1}=n+1\),同时,若 \(B_i=B_i+1\) ,则在原式中 \(\sum\) 一项的值你应当视为 0。
即为:你选择了若干位置,这些位置将原序列分隔成了若干段。代价是你选择的这些位置的元素和与每一段中所有的元素和的最大值。
给定 \(n\) 与序列 \(A\) ,求最小分隔代价。多测,\(\sum n \le 1e5\) .
思路
题目中大概的意思抽象成“最大值的最小值”,我们会想到二分答案。
则设分割后的到的代价不大于 \(val\),则要满足一下两个条件:
- 选择的断点的元素总和要小于等于 \(val\)。(条件1)
- 相邻的两个断点之间的元素和的最大值要小于等于 \(val\)。(条件2)
我们设 \(dp_i\) 为 \(i\) 位置前,满足条件2的划分方案中选择断点的元素总和的最小值。
列出 \(dp\) 方程转移式:
\[dp_i = a_i + \min_{pre_{i - 1} - pre_k <= val}\{dp_k\} \]而我们发现后面的这个式子是可以用单调列队优化成 \(O(n)\) 复杂度的。
最后返回dp[n + 1] <= val
即可。
(我们可以添加一个a[n + 1] = 0
的点来处理边界问题)。
\(code\)
#include<iostream>
#include<algorithm>
#include<queue>
#include<deque>
using namespace std;
#define int long long
const int MAXN = 1e5 + 7;
int n,dp[MAXN];
int a[MAXN],pre[MAXN];
int l,r;
deque<int> q;
bool check(int val){
for(int i = 0;i <= n + 1;i++) dp[i] = 0;
q.clear(),q.push_back(0);
for(int i = 1;i <= n + 1;i++){
while(!q.empty() && pre[i - 1] - pre[q.front()] > val){
q.pop_front();
}
dp[i] = a[i] + dp[q.front()];
while(!q.empty() && dp[q.back()] >= dp[i]) q.pop_back();
q.push_back(i);
}
return dp[n + 1] <= val;
}
signed main(){
int _;
cin>>_;
while(_--){
cin>>n;
a[n + 1] = 0;
for(int i = 1;i <= n;i++) cin>>a[i],pre[i] = pre[i - 1] + a[i];
l = 1,r = pre[n];
while(l < r){
int mid = (l + r) / 2;
if(check(mid)) r = mid;
else l = mid + 1;
}
cout<<l<<endl;
}
return 0;
}
标签:pre,Elements,val,int,sum,include,Blocking,dp
From: https://www.cnblogs.com/wyl123ly/p/18438348