这是爱奇艺的一道算法题。

题目描述请参考博客http://blog.csdn.net/sinat_30186009/article/details/52356053,在此表示感谢。

基本思路参考了以上文章,但是上面文章中的算法是java版,这是次要的,主要的问题是算法用的是原始递归思想,这样会造成计算量及其大,时间复杂度为O(n^2)。

本文旨在用C++语言解决上述问题,并且在递归的基础上进行改进,使得时间复杂度降为O(n)。其中n为高度矩阵的元素个数即row*col。

代码说明:

 输入:

  高度矩阵行数: row

  高度矩阵列数: col

  高度矩阵     : dots

 输出:

  最长滑道值:LargestSlideValue

  滑道长度矩阵:dotsLength,该矩阵最大值到最小值的路径即为最长滑道路径

  函数findLargestSlide()调用次数:Call_Of_findLargestSlide()

 #include <iostream>
#include <vector>
#include <iomanip> using namespace std; //减少寻找过程(前面几种算法都会重复计算,本算法记录每个点的最大路径,下次寻找到该点时直接加即可!降低时间复杂度关键思想)
int countfindLargestSlide=;//用于计录函数findLargestSlide调用次数
//寻找最长滑道路径(前后两点不等高)
int findLargestSlide(int x,int y,const vector<vector<int> >& dots,vector<vector<int> >& dotsLength)
{
countfindLargestSlide++; int left=y-;
int right=y+;
int up=x-;
int down=x+; int left_value=;
int right_value=;
int up_value=;
int down_value=; if(left>=&&dots[x][left]<dots[x][y]){
if(==dotsLength[x][left])//如果为0,表示该点没有被计算过,因此需要调用findLargestSlide来寻找基于该点的最长滑道
dotsLength[x][left]=left_value=findLargestSlide(x,left,dots,dotsLength);
else  //如果不为0,表示该点已经被计算过,不用重新计算,下同(降低时间复杂度关键代码)
left_value=dotsLength[x][left];
} if(right<=(int)dots[].size()-&&dots[x][right]<dots[x][y]){
if(==dotsLength[x][right])
dotsLength[x][right]=right_value=findLargestSlide(x,right,dots,dotsLength);
else
right_value=dotsLength[x][right];
} if(up>=&&dots[up][y]<dots[x][y]){
if(==dotsLength[up][y])
dotsLength[up][y]=up_value=findLargestSlide(up,y,dots,dotsLength);
else
up_value= dotsLength[up][y];
} if(down<=(int)dots.size()-&&dots[down][y]<dots[x][y]){
if(==dotsLength[down][y])
dotsLength[down][y]=down_value=findLargestSlide(down,y,dots,dotsLength);
else
down_value= dotsLength[down][y];
} int max1=left_value>=right_value?left_value:right_value;
int max2=up_value>=down_value?up_value:down_value;
int max=max1>=max2?max1:max2; dotsLength[x][y]=max+;//加上寻找点自身的路径长度1 return dotsLength[x][y];
} int main()
{
//行和列
int row,col=;
cout<<"please input row and col: ";
cin>>row>>col;
cout << "row= "<<row<<" "<<"col= "<<col << endl;
cout<<"please input dotsMatrix:"<<endl;
//矩阵(每点代表一个高度)
vector<vector<int> > dots(row,vector<int>(col,));
//输入矩阵
for(vector<int>& valrow:dots)
for(int & valcol:valrow)
cin>>valcol; //矩阵,记录每个点对应的滑道长度
vector<vector<int> > dotsLength(row,vector<int>(col,));
cout<<"--------------------"<<endl;
//输出矩阵以验证矩阵确实输入正确
cout<<"dots:"<<endl;
for(int i=;i<row;i++)
{
for(int j=;j<col;j++)
cout<<setw()<<dots[i][j];
cout<<endl;
} int max_value=;
for(int i=;i<row;i++)
{
for(int j=;j<col;j++)
{
int current_value=findLargestSlide(i,j,dots,dotsLength);
if(current_value>max_value)
{
max_value=current_value;
}
}
} cout<<"--------------------"<<endl;
cout<<"LargestSlideValue= "<<max_value<<endl;
cout<<"--------------------"<<endl;
//输出dotsLength,从该矩阵中可以方便直观地看出滑动路线(可能有多条,从最大值到最小值)
cout<<"dotsLength:"<<endl;
for(int i=;i<row;i++)
{
for(int j=;j<col;j++)
cout<<setw()<<dotsLength[i][j];
cout<<endl;
}
cout<<"--------------------"<<endl;
//输出调用findLargestSlide函数的次数,本程序时间复杂度为线性复杂度:O(n)
cout<<"Call_Of_findLargestSlide(): "<<countfindLargestSlide<<endl; return ;
}

简单对比一下改进前后的效果:

测试样例:

改进前:

改进后:

可以看出,最长滑道长度为17,改进前,函数findLargestSlide()调用841次,改进后为54次,因此我们用递归算法时一定要考虑是否可以优化。

滑道长度矩阵dotsLength中的每个值代表从该点开始滑下,可获得的滑道最长值。长度为17的最长滑道标注如下图:

意即从高度为23的顶端(dotsLength中对应的点)沿着图中蓝线一直滑到高度为1的底部(dotsLength中对应的点)。当然还有其他长度的滑道,可以从图中方便地找出来。

最后,关于时间复杂度的具体数值,时间复杂度在改进前后分别为O(n^2)和O(n),但需要注意的是,即使同样维度的矩阵,数值不同的时候函数findLargestSlide()的调用次数可能不同,但时间复杂度量级是相同的。

时间复杂度简要分析:

  改进前:粗略计算应为30*30,但是不可能每个点都会讲所有点递归计算一遍,因此最终的结果841要小于30*30=900。

  改进后:时间复杂度应该为30呀?为啥这里比30大呢?因为在main()函数内,每个元素均要计算基于它的最长滑道,均要调用一次findLargestSlide()函数,总共30个点,因此调用30次。但是要注意的是,主函数计算基于每个值的最长滑道时,并不知道这个值之前有没有被计算过,它只有按照main()函数的流程再次调用findLargestSlide()函数,进入之后才知道是否被计算过,因此要加上前面计算其他点时递归计算过该点的次数(每个点最多一次,可能0次),因此所以就比30大了,但绝对不会超过30*2-1=59(这种情况发生在计算每个点的最长滑道时都发现之前被递归计算过,除了第一个点)。因此精确的时间复杂度为:最好时为n次,最坏时为2n-1次。

举例:

测试数据(一种最坏情况):

另一种最坏情况:

以第一种最坏情况为例(都是一样的),第一次计算30对应的最长滑道时,已经递归计算了其他点的最长滑道(总共计算了30次),然而main()函数并不知道这一点呀,因此继续计算29对应的最长滑道,进去发现,哎呀!已经计算过了那太好了(但是记得从main()函数调用findLargestSlide()也要计数,加1次),同样,28,27,26,...,3,2,1,直到计算完毕,总共调用findLargestSlide()函数的次数为30+29*1=59次。

测试数据(一种最好情况):

在上面的最好情况下,每次只计算一个点对应的最长滑道,不会出现递归计算,总共调用findLargestSlide()函数的次数为30次(均为main()函数调用)。

就到这里啦,有不足的地方敬请指正,欢迎交流。

附注(文章https://blog.csdn.net/sinat_30186009/article/details/52356053内容)

1.题目描述

Michael喜欢滑雪百这并不奇怪, 因为滑雪的确很刺激。可是为了获得速度,滑的区域必须向下倾斜,而且当你滑到坡底,你不得不再次走上坡或者等待升降机来载你。Michael想知道载一个区域中最长底滑坡。区域由一个二维数组给出。数组的每个数字代表点的高度。下面是一个例子

 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

一个人可以从某个点滑向上下左右相邻四个点之一,当且仅当高度减小。在上面的例子中,一条可滑行的滑坡为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

2.思路分析

这道题实际上就是要求从最大值到最小值所能经过的最长路径,那么我们可以这么考虑,对于每一个坐标点,它到最小值的必定有一个最长路径len,那么我们只要找出所有坐标点到最小值的最长路径,然后再从中找到最大值即为所求答案。这样,我们的问题就只剩下如何求一个坐标点到最小值的最大长度len,很快我们发现每个坐标点的len必定是其上下左右坐标的len+1,这样我们就可以使用递归来解决这个问题了,详细看代码。
 

3.代码实现

 import java.util.Scanner;

 public class Main {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
String line=sc.nextLine();
String[] str=line.split(" ");
int row=Integer.parseInt(str[0]);
int column=Integer.parseInt(str[1]);
int[][] matrix=new int[row][column];
for(int i=0;imax_value){
max_value=current_value;
}
}
} System.out.println(max_value);
} private static int maxLength(int x,int y,int[][] matrix) { int left=y-1;
int right=y+1;
int up=x-1;
int down=x+1;
int left_value=0;
int right_value=0;
int up_value=0;
int down_value=0;
if(left>=0&&matrix[x][left]=0&&matrix[up][y]right_value?left_value:right_value;
int max2=up_value>down_value?up_value:down_value; return (max1>max2?max1:max2)+1;
}
}

最长滑道问题(非递归,C++)的更多相关文章

  1. 算法问题:最长滑道问题(非递归,C++)

    题目描述请参考博客http://blog.csdn.net/sinat_30186009/article/details/52356053,在此表示感谢. 基本思路参考了以上文章,但是上面文章中的算法 ...

  2. FIR系统的递归与非递归实现

    首先,因为FIR的脉冲响应是有限长,所以总是可以非递归实现的: 其次,也可以用递归系统来实现它. 以滑动平均做例子,最直观的想法就是,每次来一个新的值,丢掉最老的,加上最新的: y[n]=y[n-1] ...

  3. 面试之路(16)-归并排序详解(MergeSort)递归和非递归实现

    归并排序的概念及定义 归并排序(Merge)是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的.然后再把有序子序列合并为整体有序序列. 归并排序是建立 ...

  4. 145.Binary Tree Postorder Traversal---二叉树后序非递归遍历

    题目链接 题目大意:后序遍历二叉树. 法一:普通递归,只是这里需要传入一个list来存储遍历结果.代码如下(耗时1ms): public List<Integer> postorderTr ...

  5. 笔试算法题(56):快速排序实现之非递归实现,最小k值选择(non-recursive version, Minimal Kth Selection of Quick Sort)

    议题:快速排序实现之五(非递归实现,短序列优先处理,减少递归栈大小) 分析: 算法原理:此算法实现适用于系统栈空间不足够快速排序递归调用的需求,从而使用非递归实现快速排序算法:使用显示下推栈存储快速排 ...

  6. hdu 4850 字符串构造---欧拉回路构造序列 递归+非递归实现

    http://acm.hdu.edu.cn/showproblem.php? pid=4850 题意:构造长度为n的字符序列.使得>=4的子串仅仅出现一次 事实上最长仅仅能构造出来26^4+4- ...

  7. 【Weiss】【第03章】练习3.11:比较单链表递归与非递归查找元素

    [练习3.11] 编写查找一个单链表特定元素的程序.分别用递归和非递归实现,并比较它们的运行时间. 链表必须达到多大才能使得使用递归的程序崩溃? Answer: 实现都是比较容易的,但是实际上查找链表 ...

  8. C语言 | 栈的应用 | 非递归栈实现快排

    /* 非递归栈实现快排 */ #include <stdio.h> #include <math.h>> #include <malloc.h> #inclu ...

  9. 算法笔记_013:汉诺塔问题(Java递归法和非递归法)

    目录 1 问题描述 2 解决方案  2.1 递归法 2.2 非递归法 1 问题描述 Simulate the movement of the Towers of Hanoi Puzzle; Bonus ...

随机推荐

  1. git 的学习使用记录

    git initls -ahgit add xxxgit commit -m "some message" git statusgit loggit log --pretty=on ...

  2. Canal学习笔记(客户端)

    前言 最近公司用到Canal来做从MySQL到Tidb的数据同步,用到HA模式Canal,记录一下HA模式的工作原理. Canal的架构模式 Canal是利用binlog日志来做数据同步,canal伪 ...

  3. Java工程师如何在1个月内做好面试准备?

    作者:石杉的架构笔记 写在前面 春节长假转眼已过,即将迎来的是一年一度的金三银四跳槽季. 假如你准备在金三银四跳槽的话,那么作为一个Java工程师,应该如何利用1个月的时间,快速的为即将到来的面试进行 ...

  4. ProgressBar 进度条开源项目总结

    在Android开发中,我们不免会遇到进度条展示的需求,以下是本人之前star的开源项目,供大家参考: 一.ArcProgressBar 开源项目地址:https://github.com/zenoT ...

  5. Javascript高级编程学习笔记(65)—— 事件(9)复合事件

    复合事件 复合事件是 DOM3 中新增的一类事件,用于处理 IME 的输入序列 IME(输入法编辑器)通常用于输入物理键盘上找不到的字符,而这种输入方式通常需要同时按住多个键,但最终只输入一个字符 复 ...

  6. Javascript高级编程学习笔记(32)—— 客户端检测(1)能力检测

    能力检测 浏览器厂商虽然在实现公共接口方面投入了大量的精力 但是每种浏览器仍旧存在许多差异 为了让网页能跨浏览器的运行,对浏览器差异做的兼容处理自然无法避免 其中最常用的也就是我们现在所说的能力检测 ...

  7. JavaScript 高性能数组去重

    中午和同事吃饭,席间讨论到数组去重这一问题 我立刻就分享了我常用的一个去重方法,随即被老大指出这个方法效率不高 回家后我自己测试了一下,发现那个方法确实很慢 于是就有了这一次的高性能数组去重研究 一. ...

  8. CSS3 Gradient 渐变还能这么玩

    浏览器支持两种类型的渐变:线性渐变 (linear-gradient),径向渐变 (radial-gradient) 渐变在 CSS 中属于一种 Image 类型,可以结合 background-im ...

  9. 【code block】局部代码块+构造代码块+静态代码块

    1.局部代码块 位置:位于类的方法中 表示方法:{} 作用:控制变量的生命周期,减少内存消耗 demo: public class LocalCode { public static void mai ...

  10. C# 在窗体上可拖动控件

    最近做了一个标签打印配置功能,需要根据客户需求自定义标签格式.显示内容,这时就用到了后台生成控件,并且其控件可在窗口中进行拖动,这里仅为记录一下实现过程,方便以后使用. 结果图: 源码: using ...