当前位置: 首页 > news >正文

C++进阶课程第4期——动态规划

大家好,我是清墨,欢迎收看《C++进阶课程——动态规划dp》。

那么前几期都十分的顺利,我们直接开始~~~~

例题——抢金块

题目描述

地面上有一些格子,每个格子上面都有金块,但不同格子上的金块有不同的价值,你一次可以跳 S 至 T 步。

例如 S=2,T=4,你就可以跳 2 步、3 步或 4 步。

你从第一个格子起跳,必须跳到最后一个格子上,请你输出最多可以获得的金块的总价值。

输入格式

第 1 行是格子个数 n (n<=1000);

第 2 行是 S 和 T,保证 T 大于 S (2≤S<T≤10);

第 3 行是每个格子上的金块价值p_{i}(1≤p_{i}≤10000)。

输出格式

输出最多可以获得的金块的总价值。如果不能走到第 n 个格子,输出 0.

样例

输入数据 1

10
2 3
4 5 8 2 8 3 6 7 2 9

输出数据 1

36

样例解释

跳 1、3、5、8、10 这些位置 ,总价值:4+8+8+7+9=36。

讲解 

题目 

 我们可以先想像一下,想象一只青蛙在荷叶上跳跃,每片荷叶上有不同数量的昆虫,青蛙每次可以跳1-2片荷叶的距离,如何规划路线才能吃到最多的昆虫?

我们首先可以建立一下参数,n:荷叶总数(位置编号1-n),s/t:最小/最大跳跃步数,x[]:每个位置的昆虫数量。

这个时候我们可以定义一个数组,第i个用来存储第i个位置能得到的最多昆虫,我们把这个数组的名字叫做dp,即dp_{i}代表到达位置i时的最大收获。

dp数组的取名是这样得来的,Dynamic Programming。

我们可以思考一下,回到题目,我们要怎样求出dp_{i}呢?

我们发现dp_{i}是由dp_{i-t}~dp_{i-s}中的最大值决定的

所以可以敲出这样的代码:

for(int i=1;i<n;i++)
{if(dp[i]==0) {continue; }for(int j=s;j<=t;j++){if(i+j<=n){dp[i+j]=max(dp[i+j],dp[i]+x[i+j]);			}}	
}

这里就是动态规划的主体了。

而我们把形如dp_{i+j}=max(dp_{i+j},dp_{i}+x_{i+j})的叫做动态规划中的动态转移方程:我们设计好一些关键信息,称为状态。推演的过程里面,往往会有一个初始的状态,然后我们从已知的状态推演出新的状态,一个状态推演结束之后,这个状态在后期就不需要改变了(如果改变,那就说明你构思出来的算法是不对的,要重新构思)。写程序之前,我们要能讲清楚状态是如何转移的,这可以用一个状态转移方程来描述。

动态规划算法的解题是把一个大问题变成一个小一点点的问题,小一点点的问题又变成了一个更小一点点的问题,到了这个问题足够小的时候,我们就能知道问题的答案,然后就可以解决大一点点的问题,然后再解决更大的问题。问题的规模可以变化,但是问题的性质是保持不变的。

动态规划与分治

如果说以前学过分治的同学,可以参考如下动态规划与分治的区别:

一、共同点

  1. 分治思想‌:两者均通过分解复杂问题为子问题来解决原问题。
  2. 递归与合并‌:均可能通过递归实现子问题的分解,并合并子问题的解。

二、核心区别

  1. 子问题独立性

    • 分治法:子问题相互独立,无重叠(如归并排序的左右子数组排序)。
    • 动态规划:子问题存在重叠,需存储中间解避免重复计算(如斐波那契数列)。
  2. 最优子结构

    • 动态规划要求问题具有最优子结构(如最短路径问题),而分治法仅需子问题独立求解(如快速排序)。
  3. 实现方式

    • 分治法通常使用纯递归,可能效率较低。
    • 动态规划通过迭代或记忆化递归(如状态转移方程)优化效率。

代码

#include<bits/stdc++.h>
using namespace std;
int n,dp[1001],x[1001],s,t,ans;
int main()
{cin>>n>>s>>t;for(int i=1;i<=n;i++){cin>>x[i];}dp[1]=x[1];for(int i=1;i<n;i++){if(dp[i]==0) {continue; }for(int j=s;j<=t;j++){if(i+j<=n){dp[i+j]=max(dp[i+j],dp[i]+x[i+j]);			}}	}cout<<dp[n];return 0;
}

练习

 题目

传球游戏 

n 个同学围成一圈,编号为1到n(假设小蛮是 1 号)。游戏开始时球在小蛮手中,传球规则如下:

  1. 每次传球时,持球同学可将球传给‌左侧或右侧‌的任意一名同学12。
  2. 传球进行 m 次后停止,若球最终回到小蛮(1 号)手中,则视为一种有效传球方法12。
  3. 两种传球方法不同,当且仅当接球顺序的序列不同(例如 1→2→3→1  1→3→2→1 视为两种方法)。

输入
两个整数 n 和 m(3 ≤ n ≤ 30,1 ≤ m ≤ 30),分别表示同学人数和传球次数。

输出
一个整数,表示传球 m 次后球回到小蛮手中的不同方法总数。

示例
当 n=3、m=3 时,有效方法为 1→2→3→1 和 1→3→2→1,输出 2。

分析

我们可以进行一个分析:

假设只有4个人时,将四个人各自标记成A,B,C,D。

刚开始球可以传给B,C,D,所以传给B有1种可能,传给C也有1种可能,传给D也有1种可能。

那么,以此类推:

传球n次后传到A同学的可能数=传球n-1次后传到B同学的可能数+传球n-1次后传到D同学的可能数

A_{n}=B_{n-1}+D_{n-1},同样的B_{n}=C_{n-1}+A_{n-1}C_{n}=B_{n-1}+D_{n-1}

那么我们可以逐层遍历。(特别的,A_{0}=1

只需要考虑边界就能解决了!

代码

#include<bits/stdc++.h>
using namespace std;
int n,m;
long long a[3000][3000];
int main(){cin>>n>>m;a[1][0]=1;for(int j=1;j<=m;j++){for(int i=1;i<=n;i++){if(i>1&&i<n)a[i][j]=a[i-1][j-1]+a[i+1][j-1];else if(i==1)a[i][j]=a[n][j-1]+a[i+1][j-1];else a[i][j]=a[i-1][j-1]+a[1][j-1];}}cout<<a[1][m];return 0;
}

 

http://www.dtcms.com/a/287058.html

相关文章:

  • FastAPI遇上GraphQL:异步解析器如何让API性能飙升?
  • C++中的list(1)
  • c#中ArrayList和List的常用方法
  • 微信小程序入门实例_____从零开始 开发一个“旅行清单 ”微信小程序
  • Flutter基础(前端教程①④-data.map和assignAll和fromJson和toList)
  • 【深度学习笔记 Ⅱ】1 数据集的划分
  • Linux:多线程---深入生产消费模型环形队列生产消费模型
  • OSPF高级特性之Overflow
  • MyBatis之缓存机制详解
  • uniapp中报错:ReferenceError: FormData is not defined
  • 【安卓笔记】RxJava的Hook机制,整体拦截器
  • JavaScript空值安全深度指南
  • 加线机 和 胶带机
  • LVS 集群技术实践:NAT 与 DR 模式的配置与对比
  • 如何在HTML5页面中嵌入视频
  • 基于单片机宠物喂食器/智能宠物窝/智能饲养
  • 车载传统ECU---MCU软件架构设计指南
  • MSTP 多生成树协议
  • 零基础学后端-PHP语言(第一期-PHP环境配置)
  • 题解:CF1690G Count the Trains
  • 【C++基础】--多态
  • PortSwigger Labs 之 点击劫持利用
  • Go语言流程控制(if / for)
  • 编程研发工作日记_廖万忠_2016_2017
  • 从零构建监控系统:先“完美设计”还是先“敏捷迭代”?
  • Java Web项目Dump文件分析指南
  • 白话深度学习:一副PPT入门CNN,ResNet和Transformer
  • 闲庭信步使用图像验证平台加速FPGA的开发:第二十三课——图像直方图和灰度图像叠加的FPGA实现
  • 14-链路聚合
  • ZeroMQ中的REQ/REP模式:分布式系统的同步调用之道