入门OJ 3793: [Noip模拟题]剪草 (DP)

题目

Description

N棵小草,编号0N-1。奶牛Bessie不喜欢小草,所以Bessie要用剪刀剪草,目标是使得这N棵小草的高度总和不超过H。在第0时刻,第i棵小草的高度是h[i],接下来的每个整数时刻,会依次发生如下三个步骤:

(1)每棵小草都长高了,第i棵小草长高的高度是grow[i]

(2)Bessie选择其中一棵小草并把它剪平,这棵小草高度变为0

 注意:这棵小草并没有死掉,它下一秒还会生长的。

(3)Bessie计算一下这N棵小草的高度总和,如果不超过H,则完成任务,一切结束,否则轮到下一时刻。

你的任务是计算:最早是第几时刻,奶牛Bessie能完成它的任务?如果第0时刻就可以完成就输出0,如果永远不可
能完成,输出-1,否则输出一个最早的完成时刻。

Input

第一行,两个整数NH

第二行,N个整数,表示h[i]0 ≤ h[i] ≤ 100000

第三行,N个整数,表示grow[i]1 ≤ grow[i] ≤ 100000

1 ≤ N ≤ 500 ≤ H ≤ 1000000

Output

一个整数,最早完成时刻或-1

题解

我恨DP...

首先可以想到只有最后一次修剪才有用,因为如果说你在前面剪了它,后来又长到你不得不剪的高度,这时候你再剪它,还不如只剪它这一次,所以剪的次数不超过N

然后可以稍微贪心一下,按照生长速度排个序

然后得到转移方程

代码
#include <bits/stdc++.h>
using namespace std;
struct grass {
  int height,grow;
  inline bool operator < (const grass &n) const {
    return (this->grow < n.grow) || ((this->grow == n.grow) && (this->height > n.height));
  }
};
int n, h, ans(107), f[107][107], sum_height, sum_grow;
grass s[107];
int main(int argc, char **argv) {
  memset(f, 127, sizeof(f));
  scanf("%d%d", &n, &h);
  for (register int i(1); i <= n; ++i) scanf("%d", &s[i].height), sum_height += s[i].height;
  for (register int i(1); i <= n; ++i) scanf("%d", &s[i].grow), sum_grow += s[i].grow;
  sort(s + 1, s + 1 + n);
  f[0][0] = sum_height;
  for (register int i(1); i <= n; ++i) {
    for (register int j(i - 1); j >= 0; --j) {
      for (register int k(0); k <= j; ++k) {
        f[i][k + 1] = min(f[i][k + 1],f[j][k] + sum_grow - (k + 1) * s[i].grow - s[i].height);
        if (f[i][k + 1] <= h) ans = min(ans, k + 1);
      }
    }
  }
  printf("%d\n", ans == 107 ? -1 : ans);
}

其他

DP太难了,我都不信这是给普及组做的

转载于:https://www.cnblogs.com/forth/p/9524464.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值