艰苦打工的小蓝2

 

 

//第一种情况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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值