【华为机考真题】贪心算法解决幻兽防御战,有时急功近利反而效果更好

0 阅读3分钟

0. 前言

这道题名字叫做 幻兽防御战,名字听起来很玄幻,有点游戏的感觉,其内核是一个经典的 贪心算法 问题。如果你做过底层驱动和实时系统,就会发现这道题的逻辑其实和 实时任务调度 很相似。

下面我们进入正题。

1. 题目介绍

场景:有 n 只怪兽正在向遗迹冲来,试图破坏遗迹,小红在防守,要尽最大的努力在遗迹被破坏之前 尽可能多 的消灭怪兽。

规则 如下:

  • i 只怪兽距离遗迹的初始距离为 dist_i,速度为 speed_i
  • 小红每分钟只能发射一次弩箭,消灭任意一只怪兽,从第 0 分钟开始算起。也就是说刚开始小红就可以发射弩箭。
  • i 只怪物到达遗迹需要的时间 time_i = dist_i / speed_i
  • 如果在第 k 分钟准备射击时,存在某只未被消灭的怪兽 time_i <= k,则防守失败。

任务是计算在防守失败之前小红最多能消灭多少只怪物。

下面用两张图介绍题目的输入和要求的输出:

1. 输入.png

2. 输出.png

2.为什么急功近利是最优解?

这道题的关键在于每分钟只能射出一箭,并且所有怪物都在不断逼近遗迹。

凭直觉大家应该就能想到,小红每一箭都需要射杀到达遗迹所需时间最少的怪物,这样才能保证遗迹被摧毁时杀的怪物最多。

这就是 贪心算法

试想下面的场景:现在有两个怪物,A 需要 1 分钟就能到达遗迹,而 B 需要 3 分钟。如果小红第一剑射杀 B ,那么在一分钟时 A 就到达遗迹了,防守失败。假如小红第 0 分钟先射杀 A ,那么在第 1 分钟是还可以射杀 B,两只怪物都会死。

说白了,这其实是实时操作系统中 EDF 最早截止时间优先 调度算法的简化版。

3. 解题思路

我们需要 成对地处理 所有怪物第二行的距离和第三行的速度,这里可以使用 stringstream 进行处理。

在处理的过程中,我们需要计算出每个怪物到达遗迹所需要的时间,也就是 距离除以速度,将得到的速度存放在一个动态数组中。

sort 将动态数组中的时间从小到大进行排序。

然后进行判定,因为每分钟只能射一箭,杀死一个怪兽,所以只需要依次判断动态数组中的每个元素是否小于或者等于它的下标即可

4. 代码实现

#include <iostream>
#include <string>
#include <sstream>
#include <vector>
#include <algorithm>using namespace std;
​
int main() {
    int n;
    cin >> n;
​
    string dummy;
    getline(cin, dummy);//把n后面的换行符消耗掉
​
    vector<float> arrive_time;
​
    string line;
​
    //转换成流每次只取一个值
    getline(cin, line);//获取第二行输入
    stringstream ss1(line);
    getline(cin, line);//获取第三行输入
    stringstream ss2(line);
​
    float dist,speed;
    for(int i=0; i<n; i++)
    {
        //逐个获取每个怪兽的到达时间并存到数组中。
        float temp_time;
        ss1 >> dist;
        ss2 >> speed;
        temp_time = dist / speed;
        arrive_time.push_back(temp_time);
    }
​
    //按照升序排序
    sort(arrive_time.begin(), arrive_time.end(), [](const float &a, const float &b){
        return a < b;
    });
​
    int killed = 0;
    for(int i=0; i < n; i++)
    {
        //当某个元素小于或者等于它的下标,跳出循环
        if(arrive_time[i] <= (float)i)
        {
            break;
        }
​
        killed++;
    }
    cout << killed;//打印杀死的怪物数量
    return 0;
}

3. 通过.png