//第一种情况1和n不连续出现,这时候是线性DP
//第二种情况,1和n同时被包含,
// 由于成为环状之后边界问题难以处理所以要单独考虑
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
const int N=2e3+9;
int n,m,a[N];
ll f[N][N][2],g[N][N][2],ans=0;
int main()
{
//输入
cin>>n>>m;
for(int i=1;i<=n;i++)cin>>a[i];
//初始化
for (int i = 0; i <= n; i++)
for (int j = 0; j <= m; j++)
f[i][j][0] = f[i][j][1] = -1e18;
f[0][0][0]=0;
/*
将全部的情况都列举出来,最后只检查几种情况
理解:
(外层循环)逐个看超市中的n件商品,按照顺序每一件商品都要选择买或者不买、
(内层循环)买的时候有一份购物清单,上面写着最多买多少个
只要不超过m个就行,自己就要考虑买几个?1,2,3...m (所有的情况)
在二维dp[][]中,确定一个j,i变化表示在能够购买的商品数量一定的情况下
对于n中物品怎么买更加合适。
(状态转移方程)
1.当前的物品买:dp[i][j][1]=max(dp[i-1][j-1][1],dp[i-1][j-1][0])
因为买了该物品会占一个名额有(j-1)并且和前一个比较才能体现“好”
2.当前物品不买dp[i][j][0]=max(dp[i-1][j][1],dp[i-1][j][0])
则考虑前一个物品在什么情况下最好了,因为当前物品不买就不会对结果产生影响
这里的结果可以是最大价值或者最小的花费。
*/
//普通的线性DP
for(int i=1;i<=n;i++)
{//考虑第i个仓库干不干
for(int j=0;j<=m;j++)
{
f[i][j][0]=max(f[i-1][j][0],f[i-1][j][1]);
//当前物品不选,取决于前一个物品选或者不选
if(j>=1) f[i][j][1]=max(f[i-1][j-1][0],f[i-1][j-1][1]+a[i]);
}
}
ans=max(f[n][m][0],f[n][m][1]);//取最好结果
// 即使最后小蓝选择长度为m的区间是n,1,2,3,....
// 但是这个过程也是从1开始一直到第n个结束每次对当前仓库检查并且选择选还是不选,
// 最后选择的结果表现为从n到某个仓库(长度为m)
for(int i = 0; i <= n; i++)
for (int j = 0; j <= m; j++)
g[i][j][0] = g[i][j][1] = -1e18;
g[1][1][1]=a[1];//表示选一个仓库且第一个仓库被选择的情况下第一个仓库有贡献
//此时第一个仓库虽然先开始遍历但是它从结果上来看不是开头,
// 所以有贡献
for(int i=2;i<=n;i++)
{
for(int j=0;j<=m;j++)
{
g[i][j][0]=max(g[i-1][j][0],g[i-1][j][1]);
if(j>=1)g[i][j][1]=max(g[i-1][j-1][0],g[i-1][j-1][1]+a[i]);
}
}
ans=max(ans,g[n][m][1]);//强制选择第n个
/*
选择的过程是怎样的,为什么会强制选择呢?
比如 n,1,2,3,...;
*/
cout<<ans<<endl;
return 0;
}
//第一个部分处理序列作为一条链的情况,不考虑首尾相连。
//第二部分:处理首尾相连的情况,假设第 1 个仓库已经被选择。
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 2100; // 最大仓库数量
int n, m, a[N]; // n: 仓库数量,m: 可选择的仓库数量,a[i]: 第i个仓库的酬劳
ll f[N][N][2], g[N][N][2], ans; // 动态规划数组和最终答案
/*
f[i][j][0]:在前i个元素中,选了j个元素,且第i个元素不被选择时的最大值。
f[i][j][1]:在前i个元素中,选了j个元素,且第i个元素被选择时的最大值。
g[i][j][0]:在前i个元素中,选了j个元素,且第i个元素不被选择时的最大值,假设第1个元素已经被选择。
g[i][j][1]:在前i个元素中,选了j个元素,且第i个元素被选择时的最大值,假设第1个元素已经被选择。
*/
int main() {
// 输入仓库数量n和可选择的仓库数量m
scanf("%d%d", &n, &m);
// 输入每个仓库的酬劳
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
// 第一部分:处理1到n是一条链的情况,不考虑首尾相连
// 初始化f数组,所有状态初始值为负无穷,表示非法状态
for (int i = 0; i <= n; i++)
for (int j = 0; j <= m; j++)
f[i][j][0] = f[i][j][1] = -1e18;
// 初始状态:没有选择任何仓库时,价值为0
f[0][0][0] = 0;
// 动态规划计算f数组
for (int i = 1; i <= n; i++) { // 遍历每个仓库
for (int j = 0; j <= m; j++) { // 遍历选择的仓库数量
// 第i个仓库不被选择,继承前一个状态
f[i][j][0] = max(f[i - 1][j][0], f[i - 1][j][1]);
// 第i个仓库被选择,需要从j-1个仓库的状态转移,并加上当前仓库的酬劳
if (j >= 1) f[i][j][1] = max(f[i - 1][j - 1][0], f[i - 1][j - 1][1] + a[i]);
//第i-1个物品不选而选第i个时,第i个作为第一个不做贡献
//第i-1个物品选而选第i个时,第i个作为第二个做贡献
}
}
// 更新答案:取f数组中所有可能状态的最大值
ans = max(ans, max(f[n][m][0], f[n][m][1]));
// 第二部分:处理1和n同时选择的情况 环状问题的特殊性
// 初始化g数组,所有状态初始值为负无穷
for (int i = 0; i <= n; i++)
for (int j = 0; j <= m; j++)
g[i][j][0] = g[i][j][1] = -1e18;
// 初始状态:第1个仓库被选择,价值为a[1]
g[1][1][1] = a[1];//?
// 动态规划计算g数组
for (int i = 2; i <= n; i++) { // 从第2个仓库开始遍历
for (int j = 0; j <= m; j++) { // 遍历选择的仓库数量
// 第i个仓库不被选择,继承前一个状态
g[i][j][0] = max(g[i - 1][j][0], g[i - 1][j][1]);
// 第i个仓库被选择,需要从j-1个仓库的状态转移,并加上当前仓库的酬劳
if (j >= 1) g[i][j][1] = max(g[i - 1][j - 1][0], g[i - 1][j - 1][1] + a[i]);
}
}
// 更新答案:取g数组中所有可能状态的最大值,且强制第n个仓库被选择
ans = max(ans, g[n][m][1]);
// 输出最终答案
printf("%lld\n", ans);
return 0;
}
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
const int N=2e3+9;
int n,m,a[N];
ll f[N][N][2],g[N][N][2],ans=0;
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)cin>>a[i];
for (int i = 0; i <= n; i++)
for (int j = 0; j <= m; j++)
f[i][j][0] = f[i][j][1] = -1e18;
f[0][0][0]=0;
for(int i=1;i<=n;i++)
{//考虑第i个仓库干不干
for(int j=0;j<=m;j++)
{
f[i][j][0]=max(f[i-1][j][0],f[i-1][j][1]);
//当前物品不选,取决于前一个物品选或者不选
if(j>=1) f[i][j][1]=max(f[i-1][j-1][0],f[i-1][j-1][1]+a[i]);
}
}
ans=max(ans,max(f[n][m][0],f[n][m][1]));
for (int i = 0; i <= n; i++)
for (int j = 0; j <= m; j++)
g[i][j][0] = g[i][j][1] = -1e18;
g[1][1][1]=a[1];
for(int i=2;i<=n;i++)
{
for(int j=0;j<=m;j++)
{
g[i][j][0]=max(g[i-1][j][0],g[i-1][j][1]);
if(j>=1)g[i][j][1]=max(g[i-1][j-1][0],g[i-1][j-1][1]+a[i]);
}
}
ans=max(ans,g[n][m][1]);
cout<<ans<<endl;
return 0;
}