NOIP 1999 提高组算法题解

作者: | 更新日期:

1999 年 NOIP 提高组题解,涉及后悔贪心、LIS、搜索加背包

本文首发于公众号:天空的代码世界,微信号:tiankonguse

零、背景

今天继续分享 1999 年 NOIP 提高组题解。

截图

一、旅行家的预算

题意:告诉你始发站到终点的距离,以及中间 N 个加油站的位置与油价。
每升油能开的距离是固定的,问是否可以从始发站开到目的地,可以的话最少花费多少钱。

思路:后悔贪心。

基本思路:先把油箱加满,并记录每一滴油的油价。
走一段路,优先消耗油价最低的油。
如果遇到一个加油站,油价更便宜,则把较贵的油都后悔退还,然后购买这个更便宜的油,最后也需要把油箱加满。
到达终点后,剩余的油全部后悔退还。

根据上面的思路,可以发现我们需要一个单调双向队列,来记录每个油价有多少油。
走一段路程时,消耗队列前面便宜的油。
遇到更便宜的加油站时,把队列后面较贵的油后悔退掉。

小技巧:在终点站追加一个油价为 0 的加油站,这样可以在最后把剩余的油全部后悔换成免费的油。

N 个加油站需要排序,排序最占时间。
复杂度:O(n log(n))

二、导弹拦截

题意:爱国者防御导弹有一个缺陷,后面发射的导弹高度不能高于之前发射导弹的高度。
现在告诉你所有入侵导弹的高度,问最多可以拦截多少个导弹。

思路:最长非递减子序列。

直接套 LIS 动态规划模板即可。

LIS 状态定义:dp[i] 标识长度 i+1 的非递减子序列能够取得的最小结尾。
复杂度:O(n log(n))

vector<ll> dp;
dp.reserve(a.size());
for (ll x : a) {
  auto it = upper_bound(dp.begin(), dp.end(), x);
  if (it == dp.end()) {
    dp.push_back(x);
  } else {
    *it = x;
  }
}
return dp.size();

三、邮票面值设计

题意:有 K 种邮票,每种数量无限,最多共选择 N 张,问最大可以得到的面值 M 是多少,使得 [1, M] 之间的每一个数字都可以得到。

思路:搜索+DP。

假设 K 种邮票已经确定,则可以完全背包,来计算最大的 M。
背包大小:N * V
复杂度:O(K * N * V)

如何得到 K 种邮票呢?
由于不知道哪种组合是最优解,只能暴力枚举所有组合。

枚举策略:假设当前枚举了 A 种邮票,枚举到了 B,可以得到的最大值为 AM,则下一个邮票只能从 [B+1, AM+1] 中选择。

B+1 很容易理解,邮票从小到大枚举。
AM+1 解释:前 B 种邮票可以得到 [1,AM],下个邮票最大不能超过 AM+1,否则就无法构造出 AM+1

复杂度:O(能过)
注:这道题没有高效的算法,只能暴力枚举。

四、最后

1999 年的 NOIP 提高组的题比较简单,第一道后悔贪心,第二道 LIS 模板题,第三道是搜索题,只能暴力枚举+完全背包。
不看暴力枚举,三个都属于基本的算法了。

《完》

-EOF-

本文公众号:天空的代码世界
个人微信号:tiankonguse
公众号 ID:tiankonguse-code

本文首发于公众号:天空的代码世界,微信号:tiankonguse
如果你想留言,可以在微信里面关注公众号进行留言。

关注公众号,接收最新消息

tiankonguse +
穿越