<think>首先,用户要求用中文回答问题,所以我必须确保整个响应是中文的。
问题是关于一个编程问题:Farmer John 的奶牛分组问题。我需要解释如何解决这个问题,并给出一个 C++ 解决方案。然后,在回答结束时,必须提出 3-5 个相关问题,以 "§§相关问题§§" 开头。
关键点:
- N 头奶牛,技能水平 s_i。
- 分组:每组最多 K 头连续奶牛,奶牛不能重叠。
- 每组内的所有奶牛技能变为组内最大。
- 最大化总技能和。
输入示例:N=7, K=3, 技能序列 [1,15,7,9,2,5,10]
输出示例:84
提示:分组为 [1,15,7] -> 所有变为15, [9] ->9, [2,5,10] -> 所有变为10;和是 15*3 + 9 + 10*3 = 45 + 9 + 30 = 84。
我需要为这个问题提供一个 C++ 解决方案。
这是一个动态规划问题。我们可以定义 dp[i] 为前 i 头奶牛的最大总技能和。
状态转移:对于每个位置 i(从1到N),我们需要考虑所有可能的最后一段分组从 j 开始到 i,其中 j 满足 i - j + 1 <= K(因为每组最多 K 头奶牛)。
更精确地说:
- 对于每个 i,我们考虑 j 从 max(1, i - K + 1) 到 i,表示最后一段是从 j 到 i。
- 对于每个从 j 到 i 的段,技能水平都变为这个段的最大值。记 max_val[j][i] 为从 j 到 i 的最大值。
- 然后,dp[i] = max over j of { dp[j-1] + max_val[j][i] * (i - j + 1) }
其中 dp[0] = 0。
但计算 max_val[j][i] 需要 O(1) 时间,否则复杂度会高。如果预处理最大值的二维数组,那会是 O(N^2) 的,但 N 最大 10^4,N^2 是 10^8,可能超时。
K 最大 1000(10^3),所以对于每个 i,我们只需要检查从 i-K+1 到 i 的 j,这样每个 i 只需要 O(K) 时间,总时间 O(N*K) = 10^4 * 10^3 = 10^7,这在 C++ 中是可接受的。
所以,动态规划思路:
- dp[i] 表示前 i 头奶牛的最大和。
- 初始化 dp[0] = 0。
- 对于 i 从 1 到 N:
dp[i] = max for j from max(1, i-K+1) to i of { dp[j-1] + max_value * (i - j + 1) }
但 max_value 是从 j 到 i 的最大值。我们需要在循环中高效计算这个最大值。
由于 j 从 i 向下到 i-K+1,我们可以用一个变量来追踪当前段的最大值。
在计算 dp[i] 时,我们可以从 j = i 开始,然后 j 递减到 max(1, i-K+1),同时维护一个变量 max_val,表示从 j 到 i 的最大值。
具体步骤:
对于每个 i,设置一个临时最大值 max_val = -infinity。
然后,从 j = i 递减到 max(1, i-K+1):
- 每次 j 减少时,max_val = max(max_val, s[j]) // 因为 j 是起始点,s[j] 是新增的元素
由于 j 从 i 向下递减,当我们减少 j 时,我们添加 s[j],所以 max_val 应该更新为 max(max_val, s[j])。
例如,i 固定,j 从 i 到 low:
- 当 j = i,max_val = s[i]
- j = i-1,max_val = max(s[i], s[i-1])
- j = i-2,max_val = max(max_val, s[i-2]),以此类推。
是的。
所以算法:
- 让 s[1] 到 s[N] 存储技能水平(1-indexed)。
- dp[0] = 0
- 对于 i 从 1 到 N:
max_val = -1 // 初始化为一个很小的数
对于 j 从 i 向下到 max(1, i-K+1):
max_val = max(max_val, s[j]) // 更新当前段的最大值
len = i - j + 1 // 当前段的长度
total = dp[j-1] + max_val * len
dp[i] = max(dp[i], total) // 需要初始化 dp[i] 为负无穷或一个很小的数字
- 最终输出 dp[N]
在代码中,我们需要处理索引。由于奶牛从1到N,数组索引从1开始方便。
在C++中,我们可以使用0-indexed或1-indexed。我会使用1-indexed,这样dp[0]对应没有奶牛。
输入:第一行N和K,然后N行,每行一个技能水平。
输出:最大和。
现在,写C++代码。
包括必要的头文件:<iostream>, <vector>, <algorithm>, <climits>
使用命名空间std;
读入N和K。
然后读入N个整数到数组中。
定义dp数组大小为N+1,dp[0] = 0。
对于i从1到N:
初始化dp[i]为负无穷,但通常我们可以初始化为INT_MIN,但由于和是非负的,初始化为0也可以,但为了正确,最好初始化为一个较小的数。
实际上,dp[i] 应该至少是dp[i-1] + s[i],但最好在循环中设置dp[i] = -1,然后更新。
标准方式是:初始化dp[i] = INT_MIN,然后更新。
但INT_MIN可能太小,我们可以初始化为-1,但值可能大,所以INT_MIN是安全的。
由于技能水平是正整数,和至少是技能水平和,但dp应该正确初始化。
在内部循环中,我们设置dp[i] = max(dp[i], candidate),所以我们需要dp[i]初始化为一个很小的值。
在C++中,我们可以设置dp[0] = 0,然后对于每个i,设置dp[i] = 0,但在内部循环中会更新。
更好的方式:在进入内部循环前,设置dp[i] = -1(或INT_MIN),然后对于每个j,计算候选值并更新dp[i]。
但为了方便,我们可以在内部循环中直接用候选值更新。
伪代码:
for i in range(1, N+1):
max_val = -1 // 或0,但技能水平>0,所以-1安全
dp[i] = -1000000000; // 初始化一个很小的数
for j from i down to max(1, i-K+1): // 在C++中,索引从1开始,所以j从i downto max(1, i-K+1)
if j decreasing, when j = i, then j = i-1, etc.
max_val = max(max_val, s[j])
len = i - j + 1
candidate = dp[j-1] + max_val * len
if candidate > dp[i]
dp[i] = candidate
但max_val取决于j。由于j从i递减,我们可以如上所述。
注意:当j减少时,段是从j到i,所以当我们添加s[j]时,max_val更新。
在递减循环中:从j=i开始,max_val = s[i]
然后j=i-1,max_val = max(s[i], s[i-1])
j=i-2, max_val = max(prev_max, s[i-2]),等等。是的,正确。
在代码中,我们需要确保索引正确。
对于i,j从i downto low,其中low = max(1, i-K+1)?等一下。
如果奶牛索引从1开始,位置i。
段从j到i,长度 len = i - j + 1
约束 len <= K, 所以 j >= i - K + 1
并且 j >= 1
所以 low = max(1, i - K + 1)
在C++中,索引:如果数组s[1..N],那么j从i downto low。
但low = max(1, i - K + 1)
例如,i=1, K=3, low = max(1,1-3+1)=max(1,-1)=1, j从1到1。
i=2, low=max(1,2-3+1)=max(1,0)=1? 等等,2-3+1=0, max(1,0)应该是1?等等问题。
索引问题。
奶牛索引:1,2,3,...,N
对于位置i,考虑段结束于i。
段从j开始到i结束,j从 max(1, i-K+1) 到 i.
i-K+1 可能小于1。
例如,i=1, K=3, j 应该从1到1, 因为长度1。
i=2, K=3, j 从 max(1,2-3+1)=max(1,0)=1? 但2-3+1=0, max(1,0) 应该是1,因为索引从1开始。
在C++中,索引从1开始,所以j从 max(1, i-K+1) 到 i.
当 i-K+1 <=0 时,max(1, i-K+1) 为 1。
例如,i=2, K=3, i-K+1=0, max(1,0)=1, j from 1 to 2? 等等不对。
段从 j 开始到 i 结束,长度为 i-j+1。
当 i=2, j=1, 长度=2<=3
j=2, 长度=1<=3
所以 j 从 max(1, i-K+1) 到 i。
i-K+1:当 K=3, i=2, i-K+1=2-3+1=0,但索引至少为1,所以 low = max(1, i-K+1) = max(1,0) =1。
在 C++ 中,我们可以这样写:int low = max(1, i - K + 1);
但 i-K+1 可能为负,所以 max(1, i-K+1)。
对于 i=1, K=任意值,low = max(1,1-K+1),例如 K=3, 1-3+1=-1, max(1,-1)=1。
j 从 i 递减到 low:对于 i=1, j 从 1 到 1。
对于 i=2, low=max(1,2-K+1),如果 K=3,2-3+1=0,max(1,0)=1,j 从 2 递减到 1?递减顺序:通常我们是从高到低,所以 j 从 i 往下到 low。
j 从 i 开始然后 j-- 直到 low。
在循环中:for (int j = i; j >= low; j--)
然后对于每个 j,该段是从 j 到 i。
当前最大值:当 j=i 时,max_val = s[i]
然后 j=i-1,max_val = max(max_val, s[j]),但 s[j] 是 s[i-1],等等。
在代码中,当 j 减少时,我们向该段添加 s[j],所以 max_val 应该与 s[j] 比较。
在循环中:max_val 被初始化为一个较小的值,然后在每个 j 处更新。
在 i 的循环内部:
int max_val = -1; // 或 0,但技能 >0
for (int j = i; j >= low; j--) {
max_val = max(max_val, s[j]); // 此时段为从 j 到 i
int len = i - j + 1;
int candidate = dp[j-1] + max_val * len;
dp[i] = max(dp[i], candidate);
}
但在进入循环之前,需要将 dp[i] 初始化为一个较小的值。
更好的做法是:在循环之前将 dp[i] 初始化为 INT_MIN,然后在循环中更新。
或者,由于每个奶牛至少可以单独成组,但最好进行初始化。
在C++中,我们可以这样做。
现在,对于边界情况:当j=1时,dp[j-1] = dp[0],应该是0。
dp[0] = 0。
现在,在样例中:N=7,K=3,s = [1,15,7,9,2,5,10]
让我们模拟一下,但可能时间较长,但为了验证。
此外,在内部循环中,当j减少时,max_val会被正确更新吗?
例如,i=3(1-indexed,s[1]=1,s[2]=15,s[3]=7)
low = max(1,3-3+1)=max(1,1)=1
j 从 3 到 1:
j=3:max_val = max(-1, s[3]=7) =7,len=1,candidate = dp[2] + 7*1?但 dp[2] 还未设定,顺序很重要。
在 i 的循环中,我们是从 i=1 到 N。
所以对于 i=1:low = max(1,1-K+1),假设 K=3,low=1
j 从 1 到 1:max_val = max(-1,s[1]=1)=1,len=1,candidate = dp[0] +1*1 =0+1=1,所以 dp[1]=1
对于 i=2:low = max(1,2-3+1)=max(1,0)=1
j 从 2 到 1:
首先 j=2:max_val = max(-1,s[2]=15)=15,len=1,candidate = dp[1] +15*1 =1+15=16
然后 j=1:max_val = max(15,s[1]=1)=15?等等,当 j=1 时,段是从 j=1 到 i=2,所以是奶牛1和2,最大值是 max(s[1],s[2])=max(1,15)=15
但在这个循环中,当 j=1,max_val 是 max(之前的 max_val, s[1]),但之前的 max_val 是来自 j=2 的15,但当我们设置 j=1 时,max_val 应该是 max(15,s[1])?但 s[1] 是1,所以 max_val=15
然后 len=2,candidate = dp[0] +15*2 =0+30=30
然后 dp[2] = max(16,30) =30?但可能不对,因为对于两奶牛,如果分组,总和是30,如果单独,总和是1+15=16,所以最大是30。
但在实际情况中,对于 i=2,dp[2] 应该是前两个的最大总和。
在样例中,奶牛1和2可以分组,变为15和15,总和30;或单独,1+15=16,所以是的,30>16。
但之后当 i=3 时。
i=3:low = max(1,3-3+1)=1
j from 3 to 1:
j=3: max_val = max(-1, s[3]=7) 但因为 max_val 在循环中重置,所以对于每一个新的 j,我们需要在进入 j 循环之前设置 max_val。
在 i 的循环中,在进入 j 循环之前,设置 max_val = -1。
然后对于每个 j:
当 j=3:max_val = max(-1, s[3]) = s[3]=7,len=1,候选值 = dp[2] + 7*1 = 30 + 7 = 37
j=2:现在 j=2,段是从 2 到 3,所以 max_val = max(上一个 max_val, s[2]),但上一个 max_val 是上一个 j 的,在上面的代码中,对于每个 j,我们更新 max_val。
在 j=2:max_val = max(7, s[2]=15) = 15?但 s[2] 是 15,所以 max_val = 15
然后 len=2,段是索引 2 到 3,奶牛 2 和 3,最大值是 15,候选值 = dp[1] + 15*2 = 1 + 30 = 31
然后 j=1:max_val = max(15, s[1]=1) = 15,段是 1 到 3,len=3,候选值 = dp[0] + 15*3 = 0 + 45 = 45
所以 dp[3] = max(37, 31, 45) = 45
在样例中,前三头奶牛被分在一起,总和为 45,没错。
然后继续。
但在输出中,当 i=7,dp[7] 应该是 84。
现在,在代码中,我们需要确保索引正确。
在内部循环中,当 j 减少时,对于每个 j,段是从 j 到 i,max_val 更新为包括 s[j],但由于 j 在减少,我们添加的是左边的奶牛,所以是正确的。
在递减顺序中:当 j=i 时,只有第 i 头奶牛;j=i-1,奶牛 i-1 到 i,等等。
在 max_val 的更新中:max_val = max(max_val, s[j]),当 j 减少时,s[j] 是新的左边元素。
对于段 [j..i],当 j 减少时,s[j] 是新添加的,所以是的,max_val 更新为包括新元素。
在上面的例子中,当 i=3,j=1,段是 [1,2,3],max_val 在添加 s[1] 后为 15。
在代码中,对于每个 i,我们重置 max_val。
但在重置时,对于每个新的 j,我们重新计算 max_val?不,在循环中,我们是在遍历 j 时累加地更新 max_val。
在代码片段中:对于固定的 i,max_val 在进入 j 循环之前设置为 -1。
然后对于 j=i:max_val = max(-1, s[j]),设为 s[j]
然后对于 j=i-1:max_val = max(当前 max_val, s[j]),也就是 max(s[j..i], s[j-1]) 等等,不对。
当 j 减少时,例如,j 从 i 到 low。
在 j=current_j 时,段是 [j..i]。
当我们设置 j 时,max_val 就是 [j..i] 的最大值。
在循环中:当我们处于特定的 j,比如 j=k,那么段是 [k..i],max_val 应该是 [k..i] 的最大值。
在更新中:当我们从较高的 j 开始,比如从 j=i 开始,max_val = s[i]
然后 j=i-1,设置后 max_val = max(s[i], s[i-1])
然后 j=i-2,max_val = max(之前的 max_val, s[i-2]),也就是 max(s[i], s[i-1], s[i-2]),是的,正确。
所以代码没问题。
现在,在 C++ 中实现。
另外,对于 j,索引:s[j],以及 dp[j-1]。
当 j=1 时,j-1=0,dp[0] 应该为 0。
现在,在代码中,我们需要处理输入输出。
写代码。
首先,包含头文件。
然后,定义 N 和 K,最大 N=10000,K=1000。
使用 vector 或数组。
但 N 最大为 10^4,所以 vector 可以。
在代码中:
#include <iostream>
#include <vector>
#include <algorithm>
#include <climits>
using namespace std;
int main() {
int N, K;
cin >> N >> K;
vector<int> s(N+1); // s[1] 到 s[N]
for (int i = 1; i <= N; i++) {
cin >> s[i];
}
// dp[i] 表示前 i 头奶牛的最大和
vector<long long> dp(N+1); // 使用 long long 防止溢出,因为技能值高达 10^5,N=10^4,最大和可达 10^9,在 int 范围内?10^5 * 10^4 = 10^9,int 最大为 20 亿,所以可以用 int,但为了安全,使用 long long
dp[0] = 0; // 前 0 头奶牛的和为 0
// 对于每个 i 从 1 到 N
for (int i = 1; i <= N; i++) {
int low = max(1, i - K + 1); // 最小的 j
int max_val = -1; // 技能值是正数,所以 -1 没问题
// 在更新之前初始化 dp[i],但我们在循环中设置它
// 设置 dp[i] 为一个很小的数字
dp[i] = LLONG_MIN; // 来自 climits 的 long long 最小值
// 但也可以设为 -10e15,但使用 LLONG_MIN 更好
// 循环 j 从 i 降到 low
for (int j = i; j >= low; j--) {
// 对于每个 j,更新最大值
if (s[j] > max_val) {
max_val = s[j];
}
// max_val = max(max_val, s[j]); // 也可以
int len = i - j + 1;
// 候选值:dp[j-1] + max_val * len
long long candidate = dp[j-1] + (long long)max_val * len; // 转换为 long long 防止乘法溢出
if (candidate > dp[i]) {
dp[i] = candidate;
}
}
}
cout << dp[N] << endl;
return 0;
}
在内部循环中,当 j 变化时,max_val 会更新,但由于我们是从 j 的高值开始并向下移动,所以 max_val 是 [j..i] 的最大值,是的。
在样例中,它应该输出 84。
在提示中,分组是 [1,15,7] 变为 15、15、15,总和 45;然后 [9] 变为 9;然后 [2,5,10] 变为 10、10、10,总和 30;再加上单独的 9,总和为 45+9+30=84。
在 dp 中,对于 i=3,dp[3]=45。
然后对于 i=4(奶牛 4 技能为 9),low = max(1,4-3+1)=max(1,2)=2?i=4,K=3,low = max(1,4-3+1)=max(1,2)=2
所以 j 从 4 到 2:
j=4:max_val = s[4]=9,len=1,候选人 = dp[3] +9*1 =45+9=54
j=3:段为 [3,4],最大值为 max(7,9)=9?但 s[3]=7,s[4]=9,最大为 9。
在代码中,当 j=3:max_val 最初是用于 j=4?不,对于每个 i,在 j 循环开始时,max_val 设为 -1。
对于 i=4:
max_val = -1
j=4:max_val = max(-1,9)=9,len=1,候选人 = dp[3] +9*1 =45+9=54,dp[4] 设为 54
j=3:现在 j=3,max_val = max(9, s[3]=7) =9?段 [3,4],最大为 9,len=2,候选人 = dp[2] +9*2
但 dp[2] 是多少?之前对于 i=2,在样例中,当 N=7,但奶牛 1 和 2。
在奶牛 1:dp[1]=1(只有奶牛 1)
奶牛 2:当 i=2,low=1(K=3),j 从 2 到 1:
j=2:max_val=15(s[2]=15),len=1,候选人 = dp[1] +15*1 =1+15=16
j=1:max_val=max(15,s[1]=1)=15,len=2,候选人 = dp[0] +15*2=0+30=30,所以 dp[2]=30
奶牛 3:i=3,low=1,j 从 3 到 1:
j=3:max_val=7,len=1,候选人 = dp[2] +7*1=30+7=37
j=2:max_val=max(7,15)=15,len=2(段 [2,3]),候选人 = dp[1] +15*2=1+30=31
j=1:max_val=max(15,1)=15,len=3,候选人 = dp[0] +45=45,所以 dp[3]=45
现在 i=4(奶牛4,s[4]=9)
low = max(1,4-3+1)=max(1,2)=2
j 从 4 到 2:
首先 j=4:max_val 初始为 -1,max_val = max(-1,s[4]=9)=9,len=1,候选值 = dp[3] +9*1=45+9=54,设置 dp[4]=54
然后 j=3:max_val = max(9, s[3]=7)=9(因为段 [3,4],最大值是 max(7,9)=9),len=2,候选值 = dp[2] +9*2=30+18=48?dp[2]=30,30+18=48,比54小,所以不更新。
然后 j=2:但 low=2,所以 j 只到 2?j 从 4 到 2,包括 2。
j=2:段 [2,4],奶牛 2,3,4:s[2]=15,s[3]=7,s[4]=9,最大值是15。
在代码中,当 j=2:max_val = max(9, s[2]=15) = max(9,15)=15(因为上一个 max_val 是 j=3 时的9,但当我们设置 j=2,更新为 s[2]=15)
然后 len=3,候选值 = dp[1] +15*3=1+45=46
比54小。
所以 dp[4] 是 54。
但奶牛4 单独分组,总和为 54,但如果和奶牛3分组,但奶牛3和4最大值是9,总和为9*2=18,但加上之前的,dp[2]=30,30+18=48<54,或者只有奶牛4,45+9=54。
在最优解中,奶牛4是单独的,所以 45(前三)+9=54 没问题。
然后奶牛5,s[5]=2
i=5,low=max(1,5-3+1)=max(1,3)=3
j 从 5 到 3:
j=5:max_val=s[5]=2,len=1,候选值 = dp[4] +2*1=54+2=56
j=4:段 [4,5],最大值 max(9,2)=9,len=2,候选值 = dp[3] +9*2=45+18=63
j=3:段 [3,5],奶牛 3,4,5:s[3]=7,s[4]=9,s[5]=2,最大值是9,len=3,候选值 = dp[2] +9*3=30+27=57
所以 dp[5] = max(56,63,57) = 63
最优?奶牛4和5一起分组,都变成9,但奶牛4是9,奶牛5是2,分组后都是9,总和18,加上前三头奶牛45,总共63,但奶牛5单独的话是54+2=56,或者如果可能,但分组必须连续,所以目前最大是63。
但提示中,奶牛4是单独的,奶牛5稍后在第2,5,10一组?奶牛5是2,在序列中。
奶牛6,s[6]=5
i=6,low=max(1,6-3+1)=max(1,4)=4
j 从 6 到 4:
j=6:max_val=5,len=1,候选值 = dp[5] +5=63+5=68
j=5:段 [5,6],最大值 max(2,5)=5,len=2,候选值 = dp[4] +5*2=54+10=64
j=4: segment [4,6], cows 4,5,6: s4=9,s5=2,s6=5, max=9, len=3, candidate=dp[3] +9*3=45+27=72
所以 dp[6] = max(68,64,72) = 72
但此时,cows 4、5、6 被分在一组,全部为9,总和 27,加上前三头45,总共72。
但在最优解中,cow4 是单独分组,cow5 和 cow6 稍后与 cow7 一起分组。
现在 i=7, s[7]=10
low=max(1,7-3+1)=max(1,5)=5
j from 7 to 5:
j=7: max_val=10, len=1, candidate=dp[6] +10=72+10=82
j=6: segment [6,7], max max(5,10)=10, len=2, candidate=dp[5] +10*2=63+20=83
j=5: segment [5,7], cows 5,6,7: s5=2,s6=5,s7=10, max=10, len=3, candidate=dp[4] +10*3=54+30=84
所以 dp[7] = max(82,83,84) = 84
是的,匹配。
在 j=5 时,candidate=84,也就是将 [5,7] 分组,全部为10,但 s5=2,s6=5,s7=10,max=10,并且 dp[4]=54(前四头奶牛:前三头和单独的 cow4,45+9=54),然后加上 10*3=30,总共84。
在提示中,是 [2,5,10] 对应 cow5、6、7?cow5=2,cow6=5,cow7=10,没错。
而 cow4 是单独一组。
所以代码应该正确。
现在,在 C++ 中,我需要输出代码。
在响应中,我应该解释方法,然后提供 C++ 代码。
用户要求中文,所以用中文解释。
首先,解释动态规划方法。
然后提供代码。
最后,以“§§相关问题§§”开头提出 3-5 个相关问题。
相关问题应该基于问题和我的回答。
例如:
1. 如果 K 很大,比如 K=N,时间复杂度如何?
2. 有没有更高效的方法?
3. 如果技能值可以有负数怎么办?
等等。
现在,写响应。
</think>### 问题解答
题目要求帮助 Farmer John 将 N 头奶牛分成若干组,每组最多包含 K 头连续的奶牛,且奶牛不能重叠。分组后,每组内所有奶牛的技能水平变为该组中最高水平。目标是最大化所有奶牛的技能水平之和。
#### 解决方法
这是一个经典的动态规划问题。核心思路是定义 `dp[i]` 表示前 `i` 头奶牛(1 ≤ i ≤ N)能达到的最大技能水平和。状态转移时,对于每个位置 `i`,考虑所有可能的最后一段分组(以 `i` 结束),该段从 `j` 开始(`j` 满足 `max(1, i - K + 1) ≤ j ≤ i`,且段长度 `i - j + 1 ≤ K`)。对于每个 `j`,计算从 `j` 到 `i` 的段内最大技能水平 `max_val`,然后候选值为 `dp[j-1] + max_val * (i - j + 1)`,并取所有候选值的最大值作为 `dp[i]`。
为了高效计算 `max_val`,在固定 `i` 时,从 `j = i` 递减到 `j = max(1, i - K + 1)`,并维护一个变量 `max_val`,在每次 `j` 减小时更新它为 `max(max_val, s[j])`(`s[j]` 是奶牛 `j` 的技能水平)。这样,每个 `i` 的处理时间为 O(K),总时间复杂度为 O(N*K),在 N ≤ 10^4 和 K ≤ 10^3 的约束下可行。
边界条件:`dp[0] = 0`(前 0 头奶牛的和为 0)。
输入:第一行读入 N 和 K,接着 N 行读入奶牛技能水平序列(1-indexed)。
输出:最大技能水平和。
#### C++ 代码实现
```cpp
#include <iostream>
#include <vector>
#include <algorithm>
#include <climits>
using namespace std;
int main() {
int N, K;
cin >> N >> K;
vector<int> s(N + 1); // 技能水平数组,s[1] 到 s[N]
for (int i = 1; i <= N; i++) {
cin >> s[i];
}
// dp[i] 表示前 i 头奶牛的最大技能水平和
vector<long long> dp(N + 1); // 使用 long long 防止溢出
dp[0] = 0; // 边界条件
for (int i = 1; i <= N; i++) {
int low = max(1, i - K + 1); // 当前段的最小起始索引
int max_val = -1; // 初始化最大值为 -1(技能水平均为正数)
dp[i] = LLONG_MIN; // 初始化为极小值
// 从 i 递减到 low,考虑所有可能的最后一段
for (int j = i; j >= low; j--) {
max_val = max(max_val, s[j]); // 更新段 [j, i] 的最大值
int len = i - j + 1; // 当前段长度
long long candidate = dp[j - 1] + (long long)max_val * len; // 候选值
if (candidate > dp[i]) {
dp[i] = candidate; // 更新 dp[i]
}
}
}
cout << dp[N] << endl; // 输出结果
return 0;
}
```
#### 代码说明
- **输入处理**:读入 N 和 K,然后读入奶牛技能水平到数组 `s`(1-indexed)。
- **动态规划数组**:`dp` 数组存储最大和,`dp[0] = 0`。
- **核心循环**:对于每个 `i`,计算 `low`(段起始的最小索引),然后从 `j = i` 递减到 `low`:
- 维护 `max_val` 作为段 `[j, i]` 的最大技能水平。
- 计算候选值:`dp[j-1] + max_val * (i - j + 1)`。
- 取最大值更新 `dp[i]`。
- **输出**:`dp[N]` 即为答案。
- **复杂度**:O(N*K),满足题目约束。
样例输入 `7 3` 和技能 `[1,15,7,9,2,5,10]` 的输出为 `84`,与样例一致。