zoukankan      html  css  js  c++  java
  • poj 1088 滑雪 详解

    http://poj.org/problem?id=1088

      这是一道dp入门题,不过我一直没想明白应该怎么dp。今天,在做自己学校oj的算法基础题时看到这题,标注着dp的分类,加上我一直都比较喜欢做dp题,于是我就决心今晚要把这道入门题切了。

      题目是中文的,题目大意就免了吧……

      晚上做dp题的时候,我先是看见类似这题的一维单调增子序列,那题轻松AC了。但是,面对这个我隔了很长时间没想的二维dp题,我想了很久都想不到怎么dp。因为这题在我们学校的oj里是全部计算机专业都要知道怎么做的,所以题目下面有详细的解释,而且觉得那个题解是相当的浅显易懂。

    下面是那个解释:

    动规算法思路:
    f矩阵与原高度矩阵一样大小,f(i,j)表示从(i,j)位置开始滑,可以获得的最大滑道的长度。
    
    算法步骤:
    (1)将高度矩阵从低到高排序;
    (2)按滑点从低到高依次计算最长滑道的长度,存于f(i,j)。
    (3)f(i,j)中的最大值即原题所求。

      我依据它的描述以及我对这题的了解,解释一遍:
      我们需要的是一个用来储存原本高度的二维数组,一个用来记忆dp状态的数组,以及一个储存高度以及该高度所在的坐标的结构体数组。先说明一下,我是为了方便自己,所以除了结构体数组外其他两个数组都是开大了的。因为poj的题目中数据是0<=h<=10000,所以我用-1作为图的边界条件,dp数组正常清零。为了方便遍历四个邻接的坐标,所以我定义了四个方向向量。
      
      读入数据的时候,数据要被处理,放到相应的结构类型中。
      然后,在核心处理前,我们的dp需要一个高度单调增大的,但是对应的坐标不会改变。结构体整块的数据移动就可以保留上述预处理的高度块的坐标。这里可以直接用qsort进行对高度的快排。这个预处理是后面dp的关键。
      
      在预处理后,由低到高处理每个高度块所指示的坐标。对于每一个坐标,搜索该坐标周围的dp最大值(dp值的含义是从该点出发,最长可以滑多远),那么被处理的高度块其dp值就是上述最大值加一。然后,为什么可以这样做就是理解整道题的关键。
      可能会有人有这样的疑问,在处理的时候刚开始周围的点不是还没知道dp值吗?为什么也可以直接利用?
      解答这个问题的关键就在于,我们刚开始的时候,dp矩阵已经清零。假设从第一个坐标开始,显然,这个坐标是最矮的高度块所对应的坐标,所以在这周围四块不存在比它矮的那么一块,所以它的dp值是1。同时,我们观察到,这时周围四块的dp值都是0,所以这块必定会变为1。其他的也同理,如果周围存在比当前块高的块,那么这个块肯定是还没被赋值的,也就是默认的0。就是说,当前块无法继承这个比自己要高的块的dp值。此时为0是合理的!
      
      找到所有dp值中的最大值后输出。

      这题我wa了几次,主要是刚开始没想到周围的高度块是允许比当前的大的,所以一个大小符号,导致我浪费了不少时间。
      
    下面是我的代码:

    View Code
     1 #include "stdio.h"
     2 #include "string.h"
     3 #include "math.h"
     4 #include "stdlib.h"
     5 
     6 #define Reset(a) memset(a, 0, sizeof(a))
     7 #define MAX 1000000000
     8 
     9 int dir[4][2]={{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
    10 
    11 struct high
    12 {
    13     int x, y;
    14     int h;
    15 };
    16 
    17 int cmp(const void *_a, const void *_b)
    18 {
    19     struct high a=*(struct high *)_a;
    20     struct high b=*(struct high *)_b;
    21 
    22     return a.h-b.h;
    23 }
    24 
    25 int main()
    26 {
    27     int n, m;
    28     while (~scanf("%d%d", &n, &m))
    29     {
    30         int h[n+2][m+2], dp[n+2][m+2];
    31         struct high rec[n*m];
    32 
    33         memset(h, 255, sizeof(h));
    34         for (int i=1; i<=n; i++)
    35             for (int j=1; j<=m; j++)
    36             {
    37                 scanf("%d", &h[i][j]);
    38                 rec[(i-1)*m+j-1].h=h[i][j];
    39                 rec[(i-1)*m+j-1].x=i;
    40                 rec[(i-1)*m+j-1].y=j;
    41             }
    42         Reset(dp);
    43         qsort(rec, n*m, sizeof(struct high), cmp);
    44         int ans=0;
    45         for (int i=0, tmp=n*m; i<tmp; i++)
    46         {
    47             int max=0;
    48 
    49             for (int j=0; j<4; j++)
    50             {
    51                 if (max < dp[rec[i].x-dir[j][0]][rec[i].y-dir[j][1]]+1
    52                     && h[rec[i].x-dir[j][0]][rec[i].y-dir[j][1]]!=h[rec[i].x][rec[i].y])
    53                     max = dp[rec[i].x-dir[j][0]][rec[i].y-dir[j][1]]+1;
    54                 dp[rec[i].x][rec[i].y] = max;
    55             }
    56             if(ans<max)
    57                 ans=max;
    58         }
    59 /**
    60         for (int i=1; i<=n; i++)
    61         {
    62             for (int j=1; j<=m; j++)
    63                 printf("%4d ", dp[i][j]);
    64             puts("");
    65         }
    66 /**/
    67         printf("%d\n", ans);
    68     }
    69 
    70     return 0;
    71 }
    
    


     
     
  • 相关阅读:
    RabbitMQ学习笔记
    Eclipse下JRebel的安装和基本使用
    通过HTTP响应头让浏览器自动刷新
    CentOS6.5安装Jenkins
    Windows快捷操作技巧
    关于代码压缩混淆加密整理;
    一款优雅的小程序拖拽排序组件实现
    记一次 Mac CA证书 问题
    微信小程序-Swiper和下拉刷新组件
    WeUI Picker组件 源代码分析
  • 原文地址:https://www.cnblogs.com/LyonLys/p/poj_1088_Lyon.html
Copyright © 2011-2022 走看看