1.题目重述
给定 n n n个商店,每个商店糖果美味值不同,商店 i i i的糖果美味值为 a i a_i ai,求在至多 k k k个连续的商店的情况下,求最大糖果美味值
2.思考算法
如果直接计算糖果最大美味值,就要用双层循环,时间复杂度是
O
(
n
2
)
O(n^2)
O(n2),在
n
n
n等于
10
6
10^6
106的情况下,
n
2
n^2
n2为
10
12
10^{12}
1012,会超时,所以我们要想更优的办法
正难则反,如果我们计算删除哪些商店后,既符合至多
k
k
k个连续的商店,有保证删除的是最优的(即最小糖果美味值的),那么用糖果美味值和减删去的最小糖果美味值也就是最大糖果美味值
考虑
d
p
dp
dp,让
d
p
i
dp_i
dpi表示为第
i
i
i个商店被移除(不选)时,前
i
i
i个商店中移除商店的最小糖果美味值总和,那么要求出最小移除代价,就需要求出每
k
k
k个连续序列中的最小值,直接求时间复杂度可达
O
(
n
k
)
O(nk)
O(nk),当
k
k
k接近于
n
n
n时,还有可能超时,要快速求每
k
k
k个连续序列中的最小值,考虑单调队列
算法步骤:
1.单调队列移除元素
弹出不可能作为答案的值和过期值
2.动态转移
当前值等于前面
k
k
k个值中的最小值(就是前面距离该商店最近的糖果美味值最小的删除商店)加上删除代价
3.输出答案
即为效率和减删去的最小糖果美味值
code:
#include<bits/stdc++.h>
using namespace std;
//使用long long防止溢出,因为Ei可达10^9,N可达10^5,总和可达10^14
typedef long long ll;
ll a[1000003];//a...a[N]存储效率a[n + 1]为虚拟节点
ll dp[1000003];//dp[i]表示第i头奶牛被移除(不选)时,前i头奶牛被移除(不选)时,前i个奶牛中被移除奶牛的最小效率总和
ll sum = 0;
int main() {
//优化IO
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int T;
cin >> T;
while(T--) {
sum = 0;
memset(dp, 0, sizeof(dp));
//↑重置
int n, k;
cin >> n >> k;
for (int i = 1; i <= n; ++i) {
cin >> a[i];
sum += a[i];
}
a[n + 1] = 0;//虚拟节点n+1,效率为0,用于处理结尾的连续性
deque<int> dq;//单调队列,存储索引,对应的dp值单调递增
dq.push_back(0);
//++i用于加速
for (int i = 1; i <= n + 1; ++i) {
//删去过期值
while (!dq.empty() && dq.front() < i - k - 1) {
dq.pop_front();
}
if (!dq.empty()) {
dp[i] = dp[dq.front()] + a[i];
} else {
dp[i] = a[i];
}
//保持单调递增
while (!dq.empty() && dp[dq.back()] >= dp[i]) {
dq.pop_back();
}
dq.push_back(i);
}
cout << sum - dp[n + 1];
}
return 0;
}

94

被折叠的 条评论
为什么被折叠?



