每日一题 动态规划客栈消费

首先是动态规划的基本步骤

  1. 理解题目

    • 仔细阅读题目,理解问题的具体要求和限制条件。
    • 确定问题是否适合用动态规划来解决。动态规划通常用于解决具有重叠子问题和最优子结构的问题。
  2. 定义状态

    • 确定动态规划的状态,即 dp[i] 表示的含义。状态通常是问题的某个子问题的解。
  3. 确定状态转移方程

    • 根据问题的特点,推导出状态转移方程。状态转移方程描述了如何从一个或多个子问题的解推导出当前问题的解。
  4. 确定初始状态和边界条件

    • 设置初始状态,即 dp[0] 或其他边界值。
    • 确定边界条件,即当问题规模最小时的解。
  5. 确定遍历顺序

    • 确定更新 dp 数组的顺序,以确保在计算 dp[i] 时,所需的子问题 dp[j]j < i)已经计算过。
  6. 实现算法

    • 根据上述步骤,用代码实现动态规划算法。
  7. 测试和调试

    • 对算法进行测试,确保它能正确解决所有测试用例。
    • 调试代码,修复可能存在的错误。
  8. 优化

    • 根据需要优化算法的空间复杂度和时间复杂度。
  9. 提交代码

    • 在确保代码正确无误后,提交代码。

然后是我一直不确定的dp数组是设一维还是二维

一维 dp 数组

当你的问题可以通过一个单一的指标(如单一的整数)来描述子问题的状态时,可以使用一维数组。

例子

  • 斐波那契数列:dp[i] 表示第 i 项的斐波那契数。
  • 线性动态规划问题:如最长递增子序列(LIS),dp[i] 表示考虑到第 i 个元素时的最长递增子序列的长度。

二维 dp 数组

当你的问题需要两个或多个指标来描述子问题的状态时,应该使用二维数组。

例子

  • 背包问题:dp[i][w] 表示在不超过 w 重量的情况下,前 i 个物品能达到的最大价值。
  • 字符串编辑距离:dp[i][j] 表示将第一个字符串的前 i 个字符和第二个字符串的前 j 个字符转换成相同字符串所需的最少编辑操作次数。

多维 dp 数组

对于更复杂的问题,可能需要使用三维或更高维度的数组。

例子

  • 多重背包问题:可能需要 dp[i][j][k] 来表示考虑到第 i 种物品,使用 j 个物品,且不超过 k 重量时的最大价值。
  • 多阶段决策问题:可能需要多个维度来表示不同的阶段和决策变量。

确定 dp 数组维度的步骤:

  1. 分析问题:理解问题的结构和子问题之间的关系。
  2. 定义状态:确定描述子问题解所需的所有参数。
  3. 识别状态转移:找出如何从子问题的解构建当前问题的解。
  4. 设计 dp 数组:根据状态的定义,设计 dp 数组的结构。如果所有子问题的状态可以用一个索引表示,那么使用一维数组;如果需要两个或多个索引,那么使用二维或多维数组。

示例:

假设你正在解决一个字符串编辑距离问题,你需要考虑两个字符串的两个部分,因此你可能会定义一个二维数组 dp[i][j],其中 i 表示第一个字符串的前 i 个字符,j 表示第二个字符串的前 j 个字符。

在实际编程中,你还需要考虑数组的大小和索引的遍历顺序,以确保在计算当前状态之前,所有依赖的子状态都已经被计算过。

本问题个人解答

问题中关于题目的其实有两个变量,题目中一开始就说了,n家客栈,k种风格,还有一个是不超过p元,这是一个条件,所以我们可以设dp[i][j]表示在0-i-1客栈中风格为j的数量,然后再根据范围筛选,dp问题中最重要的就是找到转移方程,可以根据下一次方案数是上一次方案数+1得出,题目中变量有一个为色调,问题要相同的色调,所以也是一个条件。

综上题目中是有两个条件,p元和色调相同。

#include<iostream>
using namespace std;
int k[200005];   //保存颜色,用于判断
bool p[200005];  //用来保存每家是否符合小于p元这个条件
int dp[200005][105]; //dp[i][j]表示0到i-1客栈中风格为j的数量
int main()
{
    int n;   //哪一家
    int K;   //哪个颜色
    int P;   //P元这个条件
    int pr;  //记录当前客栈的最低消费
    int ans = 0;//记录个数
    cin>>n>>K>>P;
    for (int i =0,i<n;i++)
    {
        cin>>k[i]>>pr;
        if(pr<=p)
        p[i] = true;
    }
  //下面这个循环是找到各个颜色相同的客栈的数量
      for(int i=1;i<=n;i++){ //如果当前客栈满足低消,则满足的客栈的数量等于上一次满足的数量+1,i是客栈的编号
      for(int j=0;j<K;j++){//题目说有k种颜色j代表颜色的编号
      if(k[i-1]==j)//j是个编号,代表i-1号客栈的颜色是不是为j。
      dp[i][j]=dp[i-1][j]+1;
      else
      dp[i][j]=dp[i-1][j];
    }
}
  //下面的代码就是找到符合条件的客栈的数量
  for(int i=1;i<=n;i++){
    if(p[i-1])
      ans+=dp[i-1][k[i-1]];
    else {
      int j = i - 1; // 从当前客栈的前一个客栈开始
      while (j >= 0 && !p[j]) j--; // 向前搜索直到找到一个客栈,其咖啡店消费是可接受的
      ans += dp[j + 1][k[i - 1]]; // 将从那个客栈到当前客栈的同色调客栈数量累加到答案中
   }
  }
  cout<<ans<<endl;
  return 0;
}

  • 17
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值