[动规] [BJWC2008] 雷涛的小猫

[BJWC2008] 雷涛的小猫

题目描述

雷涛同学非常的有爱心,在他的宿舍里,养着一只因为受伤被救助的小猫(当然,这样的行为是违反学生宿舍管理条例的)。在他的照顾下,小猫很快恢复了健康,并且愈发的活泼可爱了。

可是有一天,雷涛下课回到寝室,却发现小猫不见了!经过一番寻找,才发现她正趴在阳台上对窗外的柿子树发呆…

在北京大学的校园里,有许多柿子树,在雷涛所在的宿舍楼前,就有 N N N 棵。并且这 N N N 棵柿子树每棵的高度都是 H H H。冬天的寒冷渐渐笼罩了大地,树上的叶子渐渐掉光了,只剩下一个个黄澄澄的柿子,看着非常喜人。而雷涛的小猫恰好非常的爱吃柿子,看着窗外树上的柿子,她十分眼馋,于是决定利用自己敏捷的跳跃能力跳到树上去吃柿子。

小猫可以从宿舍的阳台上跳到窗外任意一棵柿子树的树顶。之后,她每次都可以在当前位置沿着当前所在的柿子树向下跳 1 1 1 单位距离。当然,小猫的能力远不止如此,她还可以在树之间跳跃。每次她都可以从当前这棵树跳到另外的任意一棵,在这个过程中,她的高度会下降 Delta 单位距离。每个时刻,只要她所在的位置有柿子,她就可以吃掉。整个“吃柿子行动”一直到小猫落到地面上为止。

雷涛调查了所有柿子树上柿子的生长情况。他很想知道,小猫从阳台出发,最多能吃到多少柿子?他知道写一个程序可以很容易的解决这个问题,但是他现在懒于写任何代码。于是,现在你的任务就是帮助雷涛写一个这样的程序。

图为 N = 3 , H = 10 , D e l t a = 2 N=3, H=10, Delta=2 N=3,H=10,Delta=2 的一个例子。小猫按照图示路线进行跳跃,可以吃到最多的 8 8 8 个柿子

点击查看完整题目信息。

题解

有树的数量和高度,所以用 f ( i , j ) f(i, j) f(i,j)表示:猫在第 i 棵树的高度为 j 的地方,吃到的最多的柿子的数量。

状态转移方程:

f i , j = m a x ( f i , j , f k , j − d e l t a + T [ i ] [ j ] ) f_{i, j}=max(f_{i, j}, f_{k, j - delta} + T[i][j]) fi,j=max(fi,j,fk,jdelta+T[i][j])

上面的式子引入了第三层循环( f k , j − d e l t a f_{k, j - delta} fk,jdelta)来表示:猫从第 k k k 棵树的高度 j − D e l t a j-Delta jDelta 处跳到第 i 棵树的 j 高度。可以维护一个数组来表示在某一高度时的吃到的最大柿子数量,把时间复杂度从 O ( n 3 ) O(n^3) O(n3)降到 O ( n 2 ) O(n^2) O(n2)

代码(cpp):

#include<stdio.h>
#define MAX(a, b) ((a) > (b) ? (a) : (b))

int N, H, Delta; // N 棵,高度 H,跳到另一棵树下降 Delta
int Ni; // 第 i 棵树有 Ni 个柿子
int T[2001][2001], Tij;
int i, j, k;
int curDelta;
int pre[2001];
int dp[2001][2001];
int ans = 0;

int main()
{
  scanf("%d %d %d", &N, &H, &Delta);
  for (i = 0; i < N; ++ i)
  {
    scanf("%d", &Ni);
    for (j = 0; j < Ni; ++ j)
    {
      scanf("%d", &Tij);
      ++ T[i][Tij];
    }
  }

  for (j = 1; j < H + 1; ++ j) // 树的高度
    for (i = 0; i < N; ++ i) // 第 i 棵树
    {
      dp[i][j] = dp[i][j - 1] + T[i][j];
      if (j - Delta > 0)
        dp[i][j] = MAX(dp[i][j], pre[j - Delta] + T[i][j]);
      pre[j] = MAX(pre[j], dp[i][j]); // 在高度 j 时的吃到的最大柿子数量
    }

  for (i = 0; i < N; ++ i)
    ans = MAX(ans, dp[i][H]);
  printf("%d", ans);
  return 0;
}

注意:

  • 一棵树的某一高度会结多个柿子;
  • dp 循环时,高度循环在外,树的编号循环在内,猫横着跳,这样来保证无后效性;
  • 上面代码里维护的 pre 数组,不用担心dp[i][j] = MAX(dp[i][j], pre[j - Delta] + T[i][j])中的 pre 表示猫从第 i 棵树的 j − D e l t a j-Delta jDelta 高度跳到 ( i , j ) (i,j) (i,j),因为dp[i][j] = dp[i][j - 1] + T[i][j];表示猫从第 i 棵跳到 j 高度,这一定比pre[j - Delta] + T[i][j])更大。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值