POJ 1088 滑雪

由简单的开始学习DP,参考他人的DP题目列表,于是找最简单的题目做。照我这个年纪,做这种题目会不会很丢人,暂且不管之。

POJ 1088 题目链接

递归的定义的话,从“区域最长的”高度 H 点往下滑,滑到上下左右的某一个 H-1 点处,则 H-1 位置处也是上下左右4个点中 “区域最长的”点。

原因很简单:如果 H-1 处不是“区域最长的”点,那么可以从上下左右4个点中另外取1个“区域最长的”点作为滑下的点,加上 H 点区域会变得更长。与之前 H 点上“区域最长的”点矛盾。

则递归表达式大致是:

f(h) = f(h-1)+1 ;       if h!=1
f(h) = 1;               if h==1

递归后会造成无数次的重复,效率太低。很典型的DP问题,自底向上则会使效率快很多。

大致伪代码:

for H 点 in 从小到大排序的高度
{
    for M 点 in 矩阵中所有的点
    {
        for N 点 in M点的上下左右的4个点
        {
            if (N点的高度>M点的高度 && N点的最长区域<M点最长区域+1)
                N点的最长区域 = M点最长区域+1;
        }
    }
}
遍历矩阵中所有的点,找出区域最长的点,输出

我的源代码如下:

   1: #include <iostream>
   2: using namespace std;
   3: const int ITEM_SIZE = 10001;
   4: const int ARR_SIZE = 100;
   5:  
   6: struct ItemNode
   7: {
   8:     int hight;
   9:     int length;
  10: };
  11: ItemNode item[ARR_SIZE][ARR_SIZE];
  12: void initItem()
  13: {
  14:     for (int i=0; i<ARR_SIZE; i++)
  15:     {
  16:         for (int j=0; j<ARR_SIZE; j++)
  17:         {
  18:             item[i][j].hight = 0;
  19:             item[i][j].length = 0;
  20:         }
  21:     }
  22: }
  23:  
  24: int main()
  25: {
  26:     initItem();
  27:     int row=0, column=0;
  28:     int maxHight=0, minHight=ITEM_SIZE-1;
  29:     cin >> row >> column;
  30:     if (row<=0 || column<=0 || row>ARR_SIZE || column>ARR_SIZE)
  31:         return -1;
  32:     
  33:     for (int i=0; i<row; i++)
  34:     {
  35:         for (int j=0; j<column; j++)
  36:         {
  37:             cin >> item[i][j].hight;
  38:             maxHight = maxHight<item[i][j].hight ? item[i][j].hight : maxHight;
  39:             minHight = minHight>item[i][j].hight ? item[i][j].hight : minHight;
  40:         }
  41:     }
  42:     
  43:     for (int hight=minHight; hight<=maxHight; hight++)
  44:     {
  45:         for (int i=0; i<ARR_SIZE; i++)
  46:         {
  47:             for (int j=0; j<ARR_SIZE; j++)
  48:             {
  49:                 if (item[i][j].hight==hight)
  50:                 {
  51:                     if (i-1>=0 && item[i-1][j].hight>item[i][j].hight && item[i-1][j].length<item[i][j].length+1)
  52:                         item[i-1][j].length = item[i][j].length+1;
  53:                     if (i+1<row && item[i+1][j].hight>item[i][j].hight && item[i+1][j].length<item[i][j].length+1)
  54:                         item[i+1][j].length = item[i][j].length+1;
  55:                     if (j-1>=0 && item[i][j-1].hight>item[i][j].hight && item[i][j-1].length<item[i][j].length+1)
  56:                         item[i][j-1].length = item[i][j].length+1;
  57:                     if (j+1<column && item[i][j+1].hight>item[i][j].hight && item[i][j+1].length<item[i][j].length+1)
  58:                         item[i][j+1].length = item[i][j].length+1;
  59:                 }
  60:             }
  61:         }
  62:     }
  63:     int res = 0;
  64:     for (int i=0; i<ARR_SIZE; i++)
  65:     {
  66:         for (int j=0; j<ARR_SIZE; j++)
  67:         {
  68:             res = res<item[i][j].length ? item[i][j].length:res;
  69:         }
  70:     }
  71:     cout << res+1 << endl;
  72:     return 0;
  73: }

关于测试数据,除开题目给的一组,这里另给一组,来源

4 4
1 2 2 1
1 4 4 1
1 3 3 1
1 2 2 1
输出结果很显然,为4.
题目描述 给出一个$n\times m$的矩阵,每个位置上有一个非负整数,代表这个位置的海拔高度。一开始时,有一个人站在其中一个位置上。这个人可以向上、下、左、右四个方向移动,但是只能移动到海拔高度比当前位置低或者相等的位置上。一次移动只能移动一个单位长度。定义一个位置为“山顶”,当且仅当从这个位置开始移动,可以一直走到海拔高度比它低的位置上。请问,这个矩阵中最多有多少个“山顶”? 输入格式 第一行两个整数,分别表示$n$和$m$。 接下来$n$行,每行$m$个整数,表示整个矩阵。 输出格式 输出一个整数,表示最多有多少个“山顶”。 样例输入 4 4 3 2 1 4 2 3 4 3 5 6 7 8 4 5 6 7 样例输出 5 算法1 (递归dp) $O(nm)$ 对于这道题,我们可以使用递归DP来解决,用$f(i,j)$表示以$(i,j)$为起点的路径最大长度,那么最后的答案就是所有$f(i,j)$中的最大值。 状态转移方程如下: $$ f(i,j)=\max f(x,y)+1(x,y)是(i,j)的下一个满足条件的位置 $$ 注意:这里的状态转移方程中的$x,y$是在枚举四个方向时得到的下一个位置,即: - 向上:$(i-1,j)$ - 向下:$(i+1,j)$ - 向左:$(i,j-1)$ - 向右:$(i,j+1)$ 实现过程中需要注意以下几点: - 每个点都需要搜一遍,因此需要用双重for循环来枚举每个起点; - 对于已经搜索过的点,需要用一个数组$vis$来记录,防止重复搜索; - 在进行状态转移时,需要判断移动后的点是否满足条件。 时间复杂度 状态数为$O(nm)$,每个状态转移的时间复杂度为$O(1)$,因此总时间复杂度为$O(nm)$。 参考文献 C++ 代码 算法2 (动态规划) $O(nm)$ 动态规划的思路与递归DP类似,只不过转移方程和实现方式有所不同。 状态转移方程如下: $$ f(i,j)=\max f(x,y)+1(x,y)是(i,j)的下一个满足条件的位置 $$ 注意:这里的状态转移方程中的$x,y$是在枚举四个方向时得到的下一个位置,即: - 向上:$(i-1,j)$ - 向下:$(i+1,j)$ - 向左:$(i,j-1)$ - 向右:$(i,j+1)$ 实现过程中需要注意以下几点: - 每个点都需要搜一遍,因此需要用双重for循环来枚举每个起点; - 对于已经搜索过的点,需要用一个数组$vis$来记录,防止重复搜索; - 在进行状态转移时,需要判断移动后的点是否满足条件。 时间复杂度 状态数为$O(nm)$,每个状态转移的时间复杂度为$O(1)$,因此总时间复杂度为$O(nm)$。 参考文献 C++ 代码
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值