Teamwork

Teamwork


知识点

  • 动态规划
    • 线性 d p dp dp
  • 区间最值
    • R M Q RMQ RMQ

题目背景

F a r m e r Farmer Farmer J o h n John John 最喜欢的节日里,他想要给他的朋友们赠送一些礼物。
由于他并不擅长包装礼物,他想要获得他的奶牛们的帮助。你可能能够想到,奶牛们也不是很擅长包装礼物,而 F a r m e r Farmer Farmer J o h n John John 即将得到这一教训。

题目描述

F a r m e r Farmer Farmer J o h n John John N N N 头奶牛 ( 1 ≤ N ≤ 1 0 4 ) (1 \leq N \leq 10^{4}) (1N104) 排成一行,方便起见依次编号为 1 1 1 ~ N N N
奶牛 i i i 的包装礼物的技能水平为 S i S_{i} Si。她们的技能水平可能参差不齐,所以 F a r m e r Farmer Farmer J o h n John John 决定把她的奶牛们分成若干个小组:每一组可以包含任意 不超过 K K K 头的 连续 的奶牛 ( 1 ≤ K ≤ 1 0 3 ) (1 \leq K \leq 10^{3}) (1K103),并且一头奶牛不能属于多于一个小组。由于奶牛们会互相学习,这一组中每一头奶牛的技能水平会变成这组中 水平最高的奶牛 的技能水平。
请帮助 F a r m e r Farmer Farmer J o h n John John 求出:在他这样地安排分组的情况下,可以达到技能水平之和的最大
值。

输入格式

输入的第一行包含 N N N K K K
以下 N N N 行按照 N N N 头奶牛的排列顺序 ( 1 1 1 ~ N N N) 依次给出她们的技能水平,技能水平是一个不超过 1 0 5 10^{5} 105 的正整数。

输出格式

输出 F a r m e r Farmer Farmer J o h n John John通过将连续的奶牛进行分组可以达到的最大技能水平和。

样例
样例输入

7 3
1
15
7
9
2
5
10

样例输出

84

样例解释

在这个样例中,最优的方案是将前三头奶牛和后三头奶牛分别分为一组,中间的奶牛单独成为一组 (任意一组的奶牛数量可以小于 K K K)。
这样能够有效地将 7 7 7 头奶牛的技能水平提高至 15 、 15 、 15 、 9 、 10 、 10 、 10 15、15、15、9、10、10、10 1515159101010,和为 84 84 84


思路

这道题的数据,搜索肯定是不能全过的。
我们需要另想其他的方法。
那可不可以用 d p dp dp 呢?
如果可以,就得有 最优子结构 和 重叠子问题。


分析

我们可以设 d p [ i ] dp[i] dp[i] 表示 从 第 1 1 1 头奶牛 到 第 N N N头奶牛 的最大技能水平和。
在计算 d p [ i ] dp[i] dp[i] 的时候,可以用 j j j 枚举 i − K + 1 i - K + 1 iK+1 i i i 的奶牛,让 j j j ~ i i i 的这些奶牛分成一组,求出它们的技能水平和的最大值,再与前面的 d p [ j − 1 ] dp[j - 1] dp[j1] 相加,就是当 j j j ~ i i i为一组时的最大技能水平和。
那么怎么求 j j j ~ i i i奶牛的最大技能水平呢,不过兔兔没有用 R M Q RMQ RMQ
我们可以从 i i i ~ j j j 倒着循环枚举 S [ j ] S[j] S[j] ,与前面求出的 i i i ~ j + 1 j + 1 j+1 的最大值和 S [ j ] S[j] S[j] 做比较,就可以求出 i i i ~ j j j 奶牛的技能水平最大值了。


代码

#include <cstdio>

const int MAXN = 1e4, MAXK = 1e3;
int N, K;
int S[MAXN + 5];
int dp[MAXN + 5], MaxS[MAXN + 5];
// dp[i] 前i头奶牛 可以达到的技能水平之和 的 最大值 
// MaxS[j] 在求解 第i头奶牛 时, 它到 第j头奶牛 的最大水平 

int Max(int a, int b) { return a > b ? a : b; } // 最大值函数 Max 

int main()
{
	scanf("%d %d", &N, &K); // 输入 N 和 K 
	for (int i = 1; i <= N; i++)
		scanf("%d", &S[i]); // 输入 每个奶牛 的 水平S[i] 
	for (int i = 1; i <= N; i++)
	{
		dp[i] = dp[i - 1] + S[i]; // 当前这头奶牛一头牛一组的时候, 所达到的技能水平之和 
		MaxS[i] = S[i]; // 第i头奶牛的水平 
		for (int j = i - 1; j >= i - K + 1 && j >= 1; j--) // 只用循环到i-K+1, 因为i-K及其后面的奶牛 是不可能和 第i头奶牛 在一组的 
			MaxS[j] = Max(MaxS[j + 1], S[j]); // 求出 第i头奶牛 到 第j头奶牛 的最大水平 
		for (int j = Max(1, i - K + 1); j <= i; j++)
			dp[i] = Max(dp[i], dp[j - 1] + (i - j + 1) * MaxS[j]); // dp状态转移 
	}
	printf("%d\n", dp[N]); // 输出答案 
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值