题目描述
常州大学组织了新生寒假训练一共N天,每天训练可以获得的训练效果是Ei。但是如果连续训练超过K天,萌新们会受不了而被劝退。
现在负责人想知道,如何安排能保证萌新不会被劝退并且能获得最大的训练效果。
输入描述:
第一行:两个用空格隔开的整数:N和K,1≤N≤100000,1≤K≤N
第二行到N+1行:第i+1行有一个整数,表示第N天的训练效果是Ei,(0 <= Ei <= 1,000,000,000)
输出描述:
第一行:单个整数,表示最大的能力之和
示例1
输入
5 2
1
2
3
4
5
输出
12
说明
(除了第三天以外每天都在训练,总训练效果为1+2+4+5=12)
备注:
1≤n≤100,000
题意 : 略
分析: 这题算是很经典的单调队列优化的了,仔细说下吧,dp[i] 表示不取第i天,损失最小的收益,这个可能一开始有点不好理解吧,模拟下就ok了,我们来维护一个递增的单调队列,从而我们可以再O(1)的时间里找到最近k天,损失最少的收益,这样动态方程就为:
当 i > k 时:dp[i] = a[i] + dp[q.front()];
否则:dp[i] = a[i]
然后维护一个长度为 k 的单调队列即可
参考代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll INF_LL = 9223372036854775807LL;
const ll maxn = 1e5 + 10;
ll a[maxn];
ll dp[maxn];
int main(){
int n,k;
while (scanf("%d%d",&n,&k) == 2) {
memset (dp,0,sizeof dp);
ll sum = 0;
for (int i = 0; i < n; i++) {
scanf("%d", &a[i]);
sum += a[i];
}
deque<int> q;
for (int i = 0; i < n; i++) {
if (i > k) dp[i] = a[i] + dp[q.front()];
else dp[i] = a[i];
while(!q.empty() && dp[q.back()] > dp[i]) {
q.pop_back();
}
q.push_back(i);
if(i - q.front() > k) q.pop_front();
}
ll mi = INF_LL;
for (int i = n - k - 1; i < n; i++) {
mi = min(dp[i],mi);
}
printf("%lld\n",sum - mi);
}
return 0;
}
- 如有错误或遗漏,请私聊下UP,thx