POJ P1088动规的三种解法

Language:Default
滑雪
Time Limit: 1000MSMemory Limit: 65536K
Total Submissions: 123098Accepted: 47211

Description

Michael喜欢滑雪百这并不奇怪, 因为滑雪的确很刺激。可是为了获得速度,滑的区域必须向下倾斜,而且当你滑到坡底,你不得不再次走上坡或者等待升降机来载你。Michael想知道载一个区域中最长底滑坡。区域由一个二维数组给出。数组的每个数字代表点的高度。下面是一个例子
 1  2  3  4 516 17 18 19 615 24 25 20 714 23 22 21 813 12 11 10 9

一个人可以从某个点滑向上下左右相邻四个点之一,当且仅当高度减小。在上面的例子中,一条可滑行的滑坡为24-17-16-1。当然25-24-23-...-3-2-1更长。事实上,这是最长的一条。

Input

输入的第一行表示区域的行数R和列数C(1 <= R,C <= 100)。下面是R行,每行有C个整数,代表高度h,0<=h<=10000。

Output

输出最长区域的长度。

Sample Input

5 5
1 2 3 4 5
16 17 18 19 6
15 24 25 20 7
14 23 22 21 8
13 12 11 10 9

Sample Output

25

Source

SHTSC 2002

解法一:记忆化搜索

其实就是dfs,对每一个点都进行搜索一遍,求出每个点的最长滑雪长度,这样在每一个点中遍历找出最大的值,就是最长的滑雪长度,

但是这样做虽然好理解,有个致命的缺点,就是有很多点每一次在dfs的过程中都会访问到,这样就导致大量的重复计算,所以时间复杂度太高,交上去会超时,但是这是我们理解这道题目的很好的入口。

用一个公式简单地说:记忆化搜索=搜索的形式+动态规划的思想。
动态规划:就是一个最优化问题,先将问题分解为子问题,并且对于这些分解的子问题自身就是最优的才能在这个基础上得出我们要解决的问题的最优方案,要不然的话就能找到一个更优的解来替代这个解,得出新的最优自问题,这当然是和前提是矛盾的。动态规划不同于 贪心算法,因为贪心算法是从局部最优来解决问题,而动态规划是全局最优的。用动态规划的时候不可能在子问题还没有得到最优解的情况下就做出决策,而是必须等待子问题得到了最优解之后才对当下的情况做出决策,所以往往动态规划都可以用 一个或多个递归式来描述。而贪心算法却是先做出一个决策,然后在去解决子问题。这就是贪心和动态规划的不同。
一般遇到一个动态规划类型的问题,都先要确定最优子结构,还有重叠子问题,这两个是动态规划最大的特征,然后就是要写 动态规划的状态方程,这个步骤十分十分的重要的,写动归方程是需要一定的经验的,这可以通过训练来达到目的。接着就是要自底向上的求解问题的,先将最小规模的子问题的最优解求出,一般都用一张表来记录下求得的解,到后来遇到同样的子问题的时候就可以直接查表得到答案,最后就是通过一步一步的迭代得出最后问题的答案了。
我的理解最重要的东西就是一定会要一个数组或者其他的存储结构存储得到的子问题的解。这样就可以省很多时间,也就是典型的空间换时间
动态规划的一种变形就是记忆化搜索,就是根据动归方程写出递归式,然后在函数的开头直接返回以前计算过的结果,当然这样做也需要一个存储结构记下前面计算过的结果,所以又称为记忆化搜索。
记忆化搜索递归式动态规划
1.记忆化搜索的思想
记忆化搜索的思想是,在搜索过程中,会有很多重复计算,如果我们能记录一些状态的答案,就可以减少重复搜索量
2、记忆化搜索的适用范围
根据记忆化搜索的思想,它是解决重复计算,而不是重复生成,也就是说,这些搜索必须是在搜索扩展路径的过程中分步计算的题目,也就是“搜索答案与路径相关”的题目,而不能是搜索一个路径之后才能进行计算的题目,必须要分步计算,并且搜索过程中,一个搜索结果必须可以建立在同类型问题的结果上,也就是类似于动态规划解决的那种。
也就是说,他的问题表达,不是单纯生成一个走步方案,而是生成一个走步方案的代价等,而且每走一步,在搜索树/图中生成一个新状态,都可以精确计算出到此为止的费用,也就是,可以分步计算,这样才可以套用已经得到的答案
3、记忆化搜索的核心实现
    a. 首先,要通过一个表记录已经存储下的搜索结果,一般用哈希表实现
    b.状态表示,由于是要用哈希表实现,所以状态最好可以用数字表示,常用的方法是把一个状态连写成一个p进制数字,然后把这个数字对应的十进制数字作为状态
    c.在每一状态搜索的开始,高效的使用哈希表搜索这个状态是否出现过,如果已经做过,直接调用答案,回溯  
    d.如果没有,则按正常方法搜索
4、记忆化搜索是类似于动态规划的,不同的是,它是倒做的“递归式动态规划”。

这道题我要写的第一种写法就是记忆化搜索

应为你要求最长的滑雪坡的长度   求一个最长的下降序列,并且 你只能向 上下左右走,只能走更低的位置,这样的话,我们搜索每一个点能够下降的最长的长度,我们就可以用递归DFS去搜索,直到搜到边界返回(边界就是上下左右都不能走,然后自己的值是 1 ),但是这样搜索一定会超时,因为我们进行了太多的重复搜索。因为,当这个位置已经被搜过后,那这个点就是此时能够到最低点的最长的坡,当我们下次搜索时,还会进去继续搜索,所以记忆化搜索就出现了,我们用一个数组来保存当我们搜索过的值,就可以避免重复搜索,每当我们再搜索到这里时,我们直接用它已经搜索过的值即可。

代码如下:

#include<bits/stdc++.h>
#include<iostream>
#include<iomanip>
#include<cstring>
#include<cstdlib>typedef long long ll;
using namespace std;//记忆化搜索
int to[4][2] ={{1,0},{-1,0},{0,1},{0,-1} };
int n,m;
int high[105][105];
int maxlen[105][105];bool check(int x,int y)
{if(x>=1 && y>=1 && x<=n && y<=m){return 1;}elsereturn 0;
}dfs(int i,int j)
{if(maxlen[i][j] != 0){//被访问过,直接返回return maxlen[i][j];}maxlen[i][j] = 1;for(int k=0;k<4;k++){int x1 = i + to[k][0];int y1 = j + to[k][1];if(check(x1,y1) && high[x1][y1]<high[i][j]){maxlen[i][j] = max(maxlen[i][j],dfs(x1,y1));}}return maxlen[i][j];
}int main()
{cin>>n>>m;int ans = 1;for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){cin>>high[i][j];maxlen[i][j] = 0;}}for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){ans = max(ans,dfs(i,j));}}cout<<ans<<endl;return 0;
}

解法二:动态规划——我为人人型

简单的说就是在计算出某个点的最长滑雪长度后,就可以沿着这个点遍历四周的四个点,这样就可以一直延伸下去,而不用重复的计算某些点。

操作步骤需要从低到高计算,这样就可以保证每次得到的都是比这个点低的最长长度

步骤如下:

第一步,我们需要将所有的点按 递增序列排序。

第二步,我们从最低点更新上下左右的4个点,直到每给个点都用了。

第三步,输出我们找的过程中的最大值。

代码如下:

#include<bits/stdc++.h>
#include<iostream>
#include<iomanip>
#include<cstring>
#include<cstdlib>typedef long long ll;
using namespace std;//我为人人型
int to[4][2] ={{1,0},{-1,0},{0,1},{0,-1} };
int n,m;
int high[105][105];
int maxlen[105][105];int max1 = 1;
struct node{int x;int y;int data;
};bool cmp(node a,node b)
{return a.data < b.data;
}
bool check(int x,int y)
{if(x>=1 && y>=1 && x<=n && y<=m){return 1;}elsereturn 0;
}void maxl(node s[],int index)
{int x,y;x = s[index].x;y = s[index].y;for(int i=0;i<4;i++){int x1 = x + to[i][0];int y1 = y + to[i][1];if(check(x1,y1) && high[x1][y1]>high[x][y] && maxlen[x1][y1]<maxlen[x][y]+1){maxlen[x1][y1] = maxlen[x][y] + 1;if(max1<maxlen[x1][y1]){max1 = maxlen[x1][y1];}}}
}int main()
{cin>>n>>m;node s[10005];int k=0;for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){cin>>high[i][j];s[++k].x = i;s[k].y = j;s[k].data = high[i][j];maxlen[i][j] = 1;}}sort(s+1,s+k+1,cmp);for(int i=1;i<=k;i++){maxl(s,i);}cout<<max1<<endl;return 0;
}

解法三:动态规划——人人为我型

也就是每次从周围的四个点推出我这个点的最大高度。

这种给记忆化搜索差不多,你的目的是求每一个结点的最长升子序列,那你这个点的值取决什么呢?  是不是取决与他上下左右4个点的最长上升子序列,那用递归就好写了,但是把他转化成动态规划,这个思路就是找到状态方程,那我们把这个问题分解,就是求每个点的上升子序列,那我们找到边界,就是最低的那个点。这个人人为我型和我为人人型的唯一不一样就是这里,我为人人型 是用这个点更新其他4个点,而人人为我型就是用周围4个点更新这个一个点。找到一个周围4个点没法更新他的,不就是最低的那个点么?我们就想出了这个状态方程,从边界往上推,直到推到最后一个点(最高的点)

第一步,先进行点排序递增排序。

第二步,找到最小的点,用上下左右的点更新他,然后次小,一直到最大,(排完序后一个循环就OK)

第三步,找的过程中记录每个点的状态,并找到最大值,
代码如下:

#include<bits/stdc++.h>
#include<iostream>
#include<iomanip>
#include<cstring>
#include<cstdlib>typedef long long ll;
using namespace std;//人人为我型
int n,m;
int to[4][2] ={{1,0},{-1,0},{0,1},{0,-1}};
struct node{int x;int y;int data;
};bool cmp(node a,node b)
{return a.data <b.data;}bool check(int x,int y)
{if(x>=1 && y>=1 && x<=n && y<=m){return 1;}elsereturn 0;
}int main()
{int high[105][105];int maxlen[105][105];node s[10005];int k = 0;int ans  = 1;cin>>n>>m;for(int i=1;i<=n ;i++){for(int j=1;j<=m;j++){cin>>high[i][j];s[++k].x = i;s[k].y = j;s[k].data = high[i][j];maxlen[i][j] = 1;}}sort(s+1,s+1+k,cmp);for(int i=1;i<=k;i++){for(int j=0;j<4;j++){int x1 = s[i].x + to[j][0];int y1 = s[i].y + to[i][1];if(check(x1,y1) && high[s[i].x][s[i].y]>high[x1][y1]){maxlen[s[i].x][s[i].y] = max(maxlen[s[i].x][s[i].y],maxlen[x1][y1]+1);ans = max(ans,maxlen[s[i].x][s[i].y]);}}}cout<<ans<<endl;return 0 ;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/179796.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Junos webauth_operation.php 文件上传漏洞复现(CVE-2023-36844)

0x01 产品简介 Junos 是 Juniper Networks 生产的一款可靠的高性能网络操作系统。 0x02 漏洞概述 Junos webauth_operation.php接口处存在文件上传漏洞&#xff0c;未经身份认证的攻击者可利用 Junos 操作系统的 J-Web 服务 /webauth_operation.php 路由上传 php webshell&…

MySQL系列 - 数据类型

MySQL是一种常用的关系型数据库管理系统&#xff0c;它支持多种数据类型&#xff0c;包括整数、浮点数、字符串、日期和时间等。在本文中&#xff0c;我们将介绍MySQL中常用的数据类型及其用法。 MySQL数据类型介绍&#xff1a; 1、整数类型&#xff1a; MySQL提供了多种整数…

聚观早报 |魅族21搭载超声波指纹2.0;华为长安成立新公司

【聚观365】11月28日消息 魅族21搭载超声波指纹2.0 华为长安成立新公司 OPPO Reno11 Pro本周首销 淘宝天猫推出系列AI工具 长城汽车计划全面进入欧洲市场 魅族21搭载超声波指纹2.0 魅族官方此前已宣布&#xff0c;将于11月30日召开“2023魅族秋季无界生态发布会”&#x…

【用unity实现100个游戏之17】从零开始制作一个类幸存者肉鸽(Roguelike)游戏3(附项目源码)

文章目录 本节最终效果前言近战武器控制近战武器生成升级增加武器伤害和数量查找离主角最近的敌人子弹预制体生成子弹发射子弹参考源码完结 本节最终效果 前言 本节紧跟着上一篇&#xff0c;主要实现武器功能。 近战武器 新增Bullet&#xff0c;子弹脚本 public class Bull…

Shell编程基础 – for循环

Shell编程基础 – for循环 Shell Scripting Essentials - for Loop 大多数编程语言都有循环的概念和语句。如果想重复一个任务数十次&#xff0c;无论是输入数十次&#xff0c;还是输出数十次&#xff0c;对用户来说都不现实。 因此&#xff0c;我们考虑如何用好Bash Shell编…

单片机BootLoader是咋回事?

BootLoader的定义&#xff1a; CPU进入APP之前运行的一小段程序代码就叫做BootLoader。它是由程序员编写的&#xff0c;作用是更新应用程序。这也就说明了只有BootLoader的单片机才可以升级。有的产品有升级的需要就需要BootLoader了。 单片机的启动过程可以这么叙述&#xff…

【C++】set/multiset/map/multimap

目录 一、关联式容器 1.1 树形结构的关联式容器 二、set 2.1 set的介绍 2.2 set的使用 2.2.1 set的构造 2.2.2 set的迭代器 2.2.3 set的容量函数 2.2.4 set的功能函数 2.2.5 set的使用举例 三、multiset 3.1 multiset的介绍 3.2 multiset的使用演示 四、map 4.1…

Linux内核--内存管理(三)物理内存分页机制--kmalloc及slub机制

一、引言 二、slub机制 ------>2.1、slub分配原理slub原理 ------>2.2、slub分配原理 ------>2.3、slub释放原理 ------>2.4、SLUB分配器 三、slub数据结构 ------>3.1、kmem_cache ------>3.2、kmem_cache_cpu ------>3.3、kmem_cache_node --…

线性表之队列

文章目录 主要内容一.队列1.队列的顺序存储代码如下&#xff08;示例&#xff09;: 2.队列的链式存储3.双端队列 总结 主要内容 队列 一.队列 队列是一种先进先出&#xff08;FIFO&#xff0c;First In First Out&#xff09;的线性数据结构&#xff0c;它具有两个基本操作&a…

什么是路由抖动?该如何控制

路由器在实现不间断的网络通信和连接方面发挥着重要作用&#xff0c;具有所需功能的持续可用的路由器可确保其相关子网的良好性能&#xff0c;由于网络严重依赖路由器的性能&#xff0c;因此确保您的路由器不会遇到任何问题非常重要。路由器遇到的一个严重的网络问题是路由抖动…

Java自学过程中的学习技巧与方法分享

问题&#xff1a;请解释一下SELECT语句在单表查询中的作用&#xff0c;并结合示例代码说明如何使用SELECT语句进行单表查询。 答案&#xff1a; 在数据库中&#xff0c;使用SELECT语句进行单表查询是非常常见和基础的操作。SELECT语句用于从数据库表中检索数据&#xff0c;可…

使用 watch+$nextTick 解决Vue引入组件无法使用问题

问题描述&#xff1a; 很多时候我们都需要使用第三方组件库&#xff0c;比如Element-UI&#xff0c;Swiper 等等。 如果我们想要在这些结构中传入自己从服务器请求中获取的数据就会出现无法显示的问题。 比如我们在下面的Swiper例子中&#xff0c;我们需要new Swiper 才能让…

【华为OD题库-043】二维伞的雨滴效应-java

题目 普通的伞在二维平面世界中&#xff0c;左右两侧均有一条边&#xff0c;而两侧伞边最下面各有一个伞坠子&#xff0c;雨滴落到伞面&#xff0c;逐步流到伞坠处&#xff0c;会将伞坠的信息携带并落到地面&#xff0c;随着日积月累&#xff0c;地面会呈现伞坠的信息。 1、为了…

mysql主从复制-redis集群扩容缩容、缓存优化(缓存更新策略、穿透,击穿,雪崩)、mysql主从搭建、django实现读写分离

基于Docker实现读写分离 1 redis集群扩容缩容 1.1 集群扩容 1.2 集群缩容 2 缓存优化 2.1 缓存更新策略 2.2 穿透&#xff0c;击穿&#xff0c;雪崩 3 mysql主从搭建 4 django实现读写分离 1 redis集群扩容缩容 1.1 集群扩容 # 6台机器&#xff0c;3个节点集群# 8台机器&am…

第12关 精通K8s下的Ingress-Nginx控制器:生产环境实战配置指南

------> 课程视频同步分享在今日头条和B站 大家好&#xff0c;我是博哥爱运维&#xff0c;这节课带来k8s的流量入口ingress&#xff0c;作为业务对外服务的公网入口&#xff0c;它的重要性不言而喻&#xff0c;大家一定要仔细阅读&#xff0c;跟着博哥的教程一步步实操去理…

一文总结Java获取文件后缀名的所有方法

Java是一种应用广泛的编程语言,可以通过多种方式来实现对文件的操作&#xff0c;如文件名后缀、文件扩展名等&#xff0c;下面这篇文章主要给大家介绍了关于Java获取文件后缀名的所有方法,需要的朋友可以参考下 一、研究背景 在我们实际的项目开发中&#xff0c;我们会经常对…

HTML5语法总结大全(持续更新中~)

参考书籍&#xff1a; 《HTML与CSS3基础教程》 参考视频&#xff1a; HTML5完整教学通俗易懂 2023新版前端Web开发HTML5CSS3移动web视频教程&#xff0c;前端web入门首选黑马程序员 参考网站&#xff1a; w3school 文章目录 零.开发环境准备1.需要的工具2.Vscode所需要插件3.其…

智能优化算法应用:基于混合蛙跳算法无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于混合蛙跳算法无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于混合蛙跳算法无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.混合蛙跳算法4.实验参数设定5.算法结果6.参考…

Snagit 2024.0.1(Mac屏幕截图软件)

Snagit 2024是一款屏幕截图工具&#xff0c;可以帮助用户轻松捕获、编辑和分享屏幕截图。该工具在Mac上运行&#xff0c;旨在满足用户对于屏幕截图的各种需求。 Snagit 2024支持屏幕录制功能&#xff0c;可以录制摄像头和麦克风等外部设备&#xff0c;让用户录制更加全面的视频…

Redis基本操作及使用

&#x1f4d1;前言 本文主要是【Redis】——Redis基本操作及使用的文章&#xff0c;如果有什么需要改进的地方还请大佬指出⛺️ &#x1f3ac;作者简介&#xff1a;大家好&#xff0c;我是听风与他&#x1f947; ☁️博客首页&#xff1a;CSDN主页听风与他 &#x1f304;每日一…