从搜索丝滑过渡到动态规划的学习指南
搜索&动态规划
- 前言
- 砝码称重
- 满分代码及思路
- solution 1(动态规划)
- solution 2(BFS)
- 跳跃
- 满分代码及思路
- solution 1(动态规划)
- solution 2 (BFS)
- 积木画
- 满分代码及思路
- 动态规划思路讲解
- solution
前言
本文主要是通过一些竞赛真题 通过搜索和动态规划这两个角度 对比地去思考和解决这类令人头大的问题
我真的总感觉 并且这种感觉真的是越来越强烈 搜索我们总是正方向的去搜下一个满足需求的位置
而动态规划 我们总是去看 当前位置或者状态 能通过什么方式来走到 (状态转移) 而且往往需要取最优 这是一个逆向的过程 而且往往逆向是比较难的 所以我一开始连解析都看不懂 但是你一旦理解了这个思考方式 就会轻松很多很多
砝码称重

题目链接
满分代码及思路
solution 1(动态规划)
#include <bits/stdc++.h>
using namespace std;
const int N=110;
const int M=1e5+10;
//1首先明确状态和选择
//这题中状态就是重量和可用的砝码 选择就是放还是不放
//2.明确dp数组的定义
//dp[i][j]的定义就是对于前i个砝码是否能凑出来j的重量
//dp[3][2]=1 那意味着 对于前3个砝码能凑出2
//3.根据选择 写出状态转移方程
//放
//dp[i][j]=dp[i-1][j-wt[i]](因为是天平所以也有可能是加上当前重量)
//根据定义来理解就是 对于前i-1个砝码能凑出j-w[i]的重量 那么对于前i个砝码必定能凑出j的重量
//不放
//dp[i][j]=dp[i][j-1];
bool dp[N][M];
int n,m;
int wt[N];
int cnt=0;
int dynamic()
{for(int i=1;i<=n;i++){for(int j=0;j<=m;j++){dp[i][j]=dp[i-1][j]+dp[i-1][abs(j-wt[i])]+dp[i-1][j+wt[i]];//分别对应不放 放左边 放右边}}for(int i=1;i<=m;i++)//枚举所有重量{if(dp[n][i])//如果对于前n个砝码能凑出来i的重量 {cnt++;}}return cnt;
}
int main()
{cin>>n;for(int i=1;i<=n;i++){cin>>wt[i];m+=wt[i];//}memset(dp,0,sizeof(dp));dp[0][0]=1;//basecase 对于前0个砝码能凑出0的重量int res=dynamic();cout<<res<<endl;return 0;
}
solution 2(BFS)
#include<iostream>
#include<queue>
#include<cmath>
using namespace std;
queue<int> q;
const int N = 1e5+10;
bool sign[N];
int n,key;
int main()
{cin>>n;q.push(0);int ans = 0;for(int i=0; i<n; i++){cin>>key;queue<int> q2;while(!q.empty())//以q队列来加入,因为对每个key都进行q.front()+key和//abs(q.front()-key)进行筛选,所以不用管左边和右边是否有重复的砝码{if(!sign[q.front()+key])//这种情况相当于砝码全部放到右边{q2.push(q.front()+key);//如q2的队列sign[q.front()+key] = true;}if(!sign[abs(q.front()-key)])//左边和右边都放砝码{q2.push(abs(q.front()-key));sign[abs(q.front()-key)] = true;}q2.push(q.front());//把q.front()入到q2的队列,以便保存数据q.pop();//q出队}q = q2;//将q2赋值给q,保存数据}while(!q.empty()){if(q.front()>0) ans++;//因为q第一个入队的是0,但0又不算,所以把0排除在外q.pop();}cout<<ans;return 0;
}
跳跃

满分代码及思路
solution 1(动态规划)
#include <bits/stdc++.h>
using namespace std;
const int N=110;
int n,m;
int dp[N+1][N+1];
int val[N][N];
int dx[9]={-1,-2,-3,0,0,0,-1,-1,-2};
int dy[9]={0,0,0,-1,-2,-3,-1,-2,-1};
//其实我最开始想使用bfs来写 感觉比较容易想到 因为这题数据范围不算大而且只有一个测试点
//**********************************************************
//1 首先这题中的状态和选择是什么?
//状态就是 权值以及位置 选择就是走的方向//2 我们怎样定义DP数组?
//可以定义为 从(1,1)走到第(i,j)时的最大权值 //3 最后 我们怎么样根据选择写出状态转移方程
//我们无非就是九种选择对应九种方向 我们只需要把每种选择取最优就好
//所以总的来说 我们只需要不断计算 当下一个位置的权值会使选择之前的权值变大的时候 再更新dp数组就好
//即 dp[i][j]=dp[i+dx[k]][j+dy[k]]+val[i][j];int dynamic()
{//根据basecase以及返回值确定遍历的顺序for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){for(int k=0;k<9;k++){if(i+dx[k]>=1 && j+dy[k]>=1)//边界检查一下{if(dp[i][j]<dp[i+dx[k]][j+dy[k]]+val[i][j]){dp[i][j]=dp[i+dx[k]][j+dy[k]]+val[i][j];}}}}}return dp[n][m];
}
int main()
{cin>>n>>m;for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){cin>>val[i][j];}}memset(dp,0,sizeof(dp));//basecasedp[1][1]=val[1][1];int res=dynamic();cout<<res<<endl;return 0;
}
solution 2 (BFS)
#include <stdio.h>
#include <stdlib.h>int a[100][100]; //数据数组
int state[100][100] = { 0 }; //表示每一个坐标是否走过
int m, n; //方格图大小
int newsum; //每走一步刷新权重
int max = 0; //记录每次走到终点时权重最大值
int next[10][2] = { /* A */{0,1},{0,2},{0,3},{1,0},{1,1},{1,2},{2,0},{2,1},{2,2},{3,0} }; // 每次只能走3格距离,在网格中排列如上,A为每一步起始点void dfs(int x, int y) {int x1, y1; // 每走一步新的坐标if (x == m - 1 && y == n - 1) { //由于不断往下搜索,将超出边界的移动都舍弃了,最终一定会到达mn终点,记录下此时的权重max = max > newsum ? max : newsum;return; //仅仅为一条路,回到上一步,走没走过的路}for (int k = 0; k <= 9; k++) { //循环遍历当前坐标下下一次运动的新坐标x1 = x + next[k][0];y1 = y + next[k][1];if (x1 >= m || y1 >= n) continue; //超出边界,舍弃if (state[x1][y1] == 0) { //未超边界且没有经过该点,从该点出发,走过该点state[x1][y1] = 1; newsum = newsum + a[x1][y1]; dfs(x1, y1); // 则该点继续往下搜索state[x1][y1] = 0; newsum -= a[x1][y1]; // 回到没走过这个点的状态,更新state以及newsum}}return;
}int main()
{scanf("%d %d", &m, &n);for (int i = 0; i < m; i++) {for (int j = 0; j < n; j++)scanf("%d", &a[i][j]);}newsum += a[0][0];max = newsum;state[0][0] = 1; //数据初始化,相当于将小蓝放在(0,0)位置dfs(0, 0); printf("%d", max);return 0;
}
积木画

满分代码及思路
动态规划思路讲解
好 首先我们需要分成三步
- 1.在这题中 回答我什么是状态 什么是选择
- 回顾之前我写的文章 状态是不是就是会改变的量 选择是不是影响状态改变的量
- 但是我们好像直接地认为 状态就是剩余的格子 选择就是选择哪种积木来摆 这是不太妥当的 因为 这样我试了一下不仅我们自己都不知道这个dp数组的含义是什么 更不要说写状态转移方程了
- 所以我们需要在考虑一下 怎样是合适的
我们可以这样想,一个积木放上去后,可能会出现三种情况:第一行比第二行多出一个积木;两行积木数相等;第二行比第一行多出一个积木。
这时候有同学可能会问:为什么最多就多出一个积木呢?
因为最后的答案一定是两行积木数相等,如果两行差的多了,就不能用 L 型积木填补。只用 I 型积木填补的话,只能横着填补,我们完全可以在之前相差不超过 1 时就用 I 型积木横着填补。
因为这个画布最多只有两行 所以这时 我们就可以知道 状态就是 两行的情况 是相等还是第一行多或者第二行多
选择就是 我们如果去选取积木以及摆法 是横着还是竖着还是旋转一下
- 2. 我们如何去定义这个dp数组
- 首先我们可以给我们上述的每个状态标一个号 方便表示
- 1:前 i 列拼完后,两行积木数量相等;
0:前 i 列拼完后,第一行比第二行多 1 个积木块;
2:前 i 列拼完后,第二行比第一行多 1 个积木块。 - 比如说 dp[2][1]=5它是啥意思 我们可以通过上面的分析 大致的描述出来
- 也就是前2列处理完后 在两行数量相等的状态下 拼满画布的方案总数
- 进而我们可以去 得知dp数组的定义
dp[i][j] 表示:处理到画布第 i 列时,处于 j 状态(两行数量关系)下,拼满画布前 i 列的不同方式的数量。最终要求的答案是 dp[N][1],即拼满 2×N 画布且两行积木数量相等的方案数。
- 3 根据选择写出状态转移方程
初始化(basecase)
当没有列(i=0)时,积木都没放,自然两行相等,所以 f[0][1] = 1。
思考:我们已经有了basecase而且知道我们需要的答案是啥
那么我们是不是就可以去确定一下遍历的顺序 即从 1遍历到n 枚举每一列的情况
推导 f[i][1](两行相等):
放两个横 I 型:占两列,所以从 i-2 列的相等状态转移,即 f[i-2][1]。
放一个竖 I 型:占一列,从 i-1 列的相等状态转移,即 f[i-1][1]。
放 L 型(第一行方向):比如在第 i 和 i-1 列拼 L 型,拼之前第二行多 1 个(f[i-1][2])。
放 L 型(第二行方向):拼之前第一行多 1 个(f[i-1][0])。
最终:f[i][1] = [f[i-2][1] + f[i-1][1] + f[i-1][0] + f[i-1][2]] % 1000000007。
推导 f[i][0](第一行多 1 个):
放横 I 型在第一行:填补后,之前是第二行多 1 个(f[i-1][2])。
放 L 型:和前一列组合,拼之前两行相等(f[i-2][1])。
最终:f[i][0] = [f[i-1][2] + f[i-2][1]] % 1000000007。
推导 f[i][2](第二行多 1 个):
和 f[i][0] 对称:f[i][2] = [f[i-1][0] + f[i-2][1]] % 1000000007。
精髓 :总的方案数=所有子问题的方案数之和 希望你能理解这句话
我自己一开始想用动态规划写 其实是很难想的 代码现在看来确实很短 但真的是短小精悍 凝结了很多思考在里面 上面的大部分都是对 洛谷大佬的原文 的进一步完善和补充 希望能帮大家减少一下理解的成本和门槛!!!
solution
#include <bits/stdc++.h>
using namespace std;
const int N=1e7+10;
#define MOD 1000000007
int dp[N][3];//1代表相等 0代表第一行多 2代表第二行多
int n;
int main()
{cin>>n;memset(dp,0,sizeof(dp));//basecase dp[0][1]=1;//第0列没有积木 所以可以理解为摆满的情况有1种for(int i=1;i<=n;i++)//枚举所有列{//我们能从什么状态转移到现在第一行多?无非就是之前i-1列第二行多 但是我们在第一行横着插入了I型积木 所以导致了现在第一行多的状态 //还有就是 原来i-2列是 相等的状态 现在我们倒着插入了 L型积木 也会导致 第一行多 同理我们写出dp[i][2]的状态转移方程dp[i][0]=(dp[i-1][2]+dp[i-2][1])%MOD;dp[i][2]=(dp[i-1][0]+dp[i-2][1])%MOD;//难点就是相等的情况是由什么状态转移而来的 这是关键也是我们需要考虑的 它之前可能相等 可能不相等dp[i][1]=((dp[i-1][1]+dp[i-2][1])%MOD+(dp[i-1][0]+dp[i-1][2])%MOD)%MOD;//为了防止在计算中间就爆int 我们分布取模//(a + b + c + d) % mod = ((a + b) % mod + (c + d) % mod) % mod }cout<<dp[n][1]<<endl;return 0;
}
相关文章:
从搜索丝滑过渡到动态规划的学习指南
搜索&动态规划 前言砝码称重满分代码及思路solution 1(动态规划)solution 2(BFS) 跳跃满分代码及思路solution 1(动态规划)solution 2 (BFS) 积木画满分代码及思路动态规划思路讲解solution 前言 本文主要是通过一些竞赛真题…...
(一)栈结构、队列结构
01-线性结构-数组-栈结构 线性结构(Linear List)是由n(n>0)个数据元素(结点) a[0], a[1], a[2], a[3],...,a[n-1]组成的有限序列 数组 通常数组的内存是连续的,所以在知道数组下标的情况下,访问效率是…...
AWS SNS深度解析:构建高可用、可扩展的云原生消息通信解决方案
引言 在云原生架构中,高效的消息通信是系统解耦、实时响应的核心需求。AWS Simple Notification Service(SNS)作为一款全托管的发布/订阅(Pub/Sub)服务,为开发者提供了灵活、可靠的消息分发能力。本文将从…...
MySQL基础 [五] - 表的增删查改
目录 Create(insert) Retrieve(select) where条件 编辑 NULL的查询 结果排序(order by) 筛选分页结果 (limit) Update Delete 删除表 截断表(truncate) 插入查询结果(insertselect&…...
4.7学习总结 可变参数+集合工具类Collections+不可变集合
可变参数: 示例: public class test {public static void main(String[] args) {int sumgetSum(1,2,3,4,5,6,7,8,9,10);System.out.println(sum);}public static int getSum(int...arr){int sum0;for(int i:arr){sumi;}return sum;} } 细节:…...
OpenGL学习笔记(简介、三角形、着色器、纹理、坐标系统、摄像机)
目录 简介核心模式与立即渲染模式状态机对象GLFW和GLAD Hello OpenGLTriangle 三角形顶点缓冲对象 VBO顶点数组对象 VAO元素缓冲对象 EBO/ 索引缓冲对象 IEO 着色器GLSL数据类型输入输出Uniform 纹理纹理过滤Mipmap 多级渐远纹理实际使用方式纹理单元 坐标系统裁剪空间 摄像机自…...
vmware虚拟机上Ubuntu或者其他系统无法联网的解决方法
一、检查虚拟机是否开启了网络服务 打开方式:控制面板->-管理工具--->服务 查找 VMware DHCP Service 和VMware NAT Service ,确保这两个服务已经启动。如下图,没有启动就点击启动。 二、设置网络类型 我们一般使用前两种多一些&…...
OpenVLA-OFT——微调VLA时加快推理的三大关键设计:支持动作分块的并行解码、连续动作表示以及L1回归(含输入灵活化及对指令遵循的加强)
前言 25年3.26日,这是一个值得纪念的日子,这一天,我司「七月在线」的定位正式升级为了:具身智能的场景落地与定制开发商 ,后续则从定制开发 逐步过渡到 标准产品化 比如25年q2起,在定制开发之外࿰…...
Linux脚本基础详解
一、基础知识 Linux 脚本主要是指在 Linux 系统中编写的用于自动化执行任务的脚本程序,其中最常用的便是 Bash 脚本。下面我们将从语法、使用方法和示例三个方面详细讲解 Linux 脚本。 1. 脚本简介 定义:Linux 脚本是一系列命令的集合,可以…...
LabVIEW 油井动液面在线监测系统
项目背景 传统油井动液面测量依赖人工现场操作,面临成本高、效率低、安全风险大等问题。尤其在偏远地区或复杂工况下,测量准确性与时效性难以保障。本系统通过LabVIEW虚拟仪器技术实现硬件与软件深度融合,为油田智能化转型提供实时连续监测解…...
泛微ECOLOGY9 解决文档中打开发票类PDF文件无内容的配置方法
解决文档中打开发票类PDF文件无内容的配置方法 情况如下: 如果OA文档中打开的PDF文件如下图这样空白的,那么可以试试下面的方法进行解决。 解决方法: 在OA安装目录中找到 ecology/WEB-INF/prop/docpreview.properties 配置文件ÿ…...
大模型RAG项目实战-知识库问答助手v1版
安装 Ollama 根据官网指导,安装对应版本即可。 下载安装指导文档: handy-ollama/docs/C1/1. Ollama 介绍.md at main datawhalechina/handy-ollama 注意:在 Windows 下安装 Ollama 后,强烈建议通过配置环境变量来修改模型存储…...
统计子矩阵
1.统计子矩阵 - 蓝桥云课 统计子矩阵 问题描述 给定一个 NM 的矩阵 A,请你统计有多少个子矩阵(最小 11,最大 NM)满足子矩阵中所有数的和不超过给定的整数 K? 输入格式 第一行包含三个整数 N,M 和 K。 …...
Vue.js 实现下载模板和导入模板、数据比对功能核心实现。
在前端开发中,数据比对是一个常见需求,尤其在资产管理等场景中。本文将基于 Vue.js 和 Element UI,通过一个简化的代码示例,展示如何实现“新建比对”和“开始比对”功能的核心部分。 一、功能简介 我们将聚焦两个核心功能&…...
C++第1讲:基础语法;通讯录管理系统
黑马程序员匠心之作|C教程从0到1入门编程,学习编程不再难_哔哩哔哩_bilibili 对应的笔记: https://github.com/AccumulateMore/CPlusPlus 标签: C&C | welcome to here 一、C初识 1.1.注释 1.2.变量 1.3.常量:记录程序中不可更改的数据 1.4.关…...
关于Spring MVC处理JSON数据集的详细说明,涵盖如何接收和发送JSON数据,包含代码示例和总结表格
以下是关于Spring MVC处理JSON数据集的详细说明,涵盖如何接收和发送JSON数据,包含代码示例和总结表格: 1. 核心机制 Spring MVC通过以下方式支持JSON数据的传输: 接收JSON数据:使用RequestBody注解将HTTP请求体中的J…...
MySQL 隐式转换与模糊匹配的索引使用分析
MySQL 隐式转换与模糊匹配的索引使用分析 MySQL服务版本字段结构索引结构查询分析int索引查询varchar 索引查询 like 匹配总结 MySQL服务版本 版本信息:Server version: 8.0.30 MySQL Community Server - GPL 字段结构 mysql> desc connection; -------------…...
DNS服务(Linux)
DNS 介绍 dns,Domain Name Server,它的作用是将域名解析为 IP 地址,或者将IP地址解析为域名。 这需要运行在三层和四层,也就是说它需要使用 TCP 或 UDP 协议,并且需要绑定端口,53。在使用时先通过 UDP 去…...
【愚公系列】《高效使用DeepSeek》058-选题策划
🌟【技术大咖愚公搬代码:全栈专家的成长之路,你关注的宝藏博主在这里!】🌟 📣开发者圈持续输出高质量干货的"愚公精神"践行者——全网百万开发者都在追更的顶级技术博主! 👉 江湖人称"愚公搬代码",用七年如一日的精神深耕技术领域,以"…...
Python高阶函数-filter
1. 基本概念 filter() 是Python内置的高阶函数,用于过滤序列中的元素。它接收一个函数和一个可迭代对象作为参数,返回一个迭代器,包含使函数返回True的所有元素。 filter(function, iterable)2. 工作原理 惰性计算:filter对象是…...
✅ Ultralytics YOLO验证(Val)时自动输出COCO指标(AP):2025最新配置与代码详解 (小白友好 + B站视频)
✅ YOLO获取COCO指标(3):验证(Val) 启用 COCO API 评估(自动输出AP指标)| 发论文必看! | Ultralytics | 小白友好 文章目录 一、问题定位二、原理分析三、解决方案与实践案例步骤 1: 触发 COCO JSON 保存步骤 2: 确保 self.is_coc…...
MySql表达式中字符串类型与整型的隐式转换
隐式转换 当运算符与不同类型的操作数一起使用时,会发生类型转换以使操作数兼容。某些转换是隐式发生的。例如,MySQL 会根据需要自动将字符串转换为数字,反之亦然。 mysql> SELECT 11;-> 2 mysql> SELECT CONCAT(2, test);-> 2…...
拍摄的婚庆视频有些DAT的视频文件打不开怎么办
3-12 现在的婚庆公司大多提供结婚的拍摄服务,或者有一些第三方公司做这方面业务,对于视频拍摄来说,有时候会遇到这样一种问题,就是拍摄下来的视频文件,然后会有一两个视频文件是损坏的,播放不了࿰…...
Zephyr与Linux核心区别及适用领域分析
一、核心定位与目标场景 特性Zephyr RTOSLinux目标领域物联网终端、实时控制系统(资源受限设备)服务器、桌面系统、复杂嵌入式设备(如路由器)典型硬件MCU(ARM Cortex-M, RISC-V),内存<1MBMP…...
图灵逆向——题一-动态数据采集
目录列表 过程分析代码实现 过程分析 第一题比较简单,直接抓包即可,没有任何反爬(好像头都不用加。。。) 代码实现 答案代码如下: """ -*- coding: utf-8 -*- File : .py author : 鲨鱼爱兜兜 T…...
【新人系列】Golang 入门(十二):指针和结构体 - 上
✍ 个人博客:https://blog.csdn.net/Newin2020?typeblog 📝 专栏地址:https://blog.csdn.net/newin2020/category_12898955.html 📣 专栏定位:为 0 基础刚入门 Golang 的小伙伴提供详细的讲解,也欢迎大佬们…...
Day20 -实例:红蓝队优秀集成式信息打点工具的配置使用
一、自动化-企业查询 ----ENScan 原理:集成企查查、爱企查、chinaz等,剑指hw/src。 1)首次使用先创建config文件 确认一下生成了 2)配置cookie 各个平台不一样,根据github作者的教程来【放入github收藏夹了】 我这…...
MySQL学习笔记五
第七章数据过滤 7.1组合WHERE子句 7.1.1AND操作符 输入: SELECT first_name, last_name, salary FROM employees WHERE salary < 4800 AND department_id 60; 输出: 说明:MySQL允许使用多个WHERE子句,可以以AND子句或OR…...
Python爬虫第5节-urllib的异常处理、链接解析及 Robots 协议分析
目录 一、处理异常 1.1 URLError 1.2 HTTPError 二、解析链接 2.1 urlparse() 2.2 urlunparse() 2.3 urlsplit() 2.4 urlunsplit() 2.5 urljoin() 2.6 urlencode() 2.7 parse_qs() 2.8 parse_qsl() 2.9 quote() 2.10 unquote() 三、分析网站Robots协议 3.1 R…...
26届Java暑期实习面经,腾讯视频一面
短链接的生成原理 如何解决短链接生成的哈希冲突问题 如何加快从短链接到原链接的重定向过程 TCP 和 UDP 协议 如何理解 TCP 是面向连接的 为什么 TCP 的握手是 3 次 IO 模式 是否有真正写过一个底层的 Socket 通信 MySQL 的事务隔离级别 MVCC 机制 什么叫服务的并行 为什么能基…...
