Bootstrap

贪心算法:经典活动安排问题

来源:加码未来模拟试卷

这是一个经典的活动安排问题,本质上是一个贪心算法问题。本文给出了C++ 和Python的代码实现。C++代码解析对不同数据结构的实现进行对比说买,总结了C++内存管理的关键。Python代码解析还给出了测试用例。最后专门用一节对贪心算法的应用场景进行了分析,列出了适用和不适用场景。

题目描述

放假了,小浩在放假时有许多活动可以选择,每个活动都有一个开始时间和结束时间,如果选择了某个活动,那么从开始到结束的这段时间都不能做其他事情了。假如某项活动的开始时间刚好等于另一项活动的结束时间,是可以都参加的。现在给出各项活动的时间安排,请问,小浩最多可以参加几项活动。

输入

第一行一个整数 n n n,表示活动数目接下来 n n n 行,每行两个整数 s s s t t t 表示活动的起始时间和终止时间。
1 < = n < = 1000 1<=n<=1000 1<=n<=1000
0 < s < t < 1 0 9 0<s<t<10^9 0<s<t<109

输出

一个整数表示答案

输入样例

4
1 3
4 6
2 5
1 7

输出样例

2

算法分析

算法思想

这是一个经典的活动安排问题,解决方案使用了贪心算法的思想,主要步骤如下:

  1. 首先将所有活动按照结束时间排序
  2. 选择第一个结束最早的活动
  3. 然后遍历剩余活动,如果当前活动的开始时间大于等于上一个选择的活动的结束时间,就选择这个活动
  4. 统计总共选择的活动数量

贪心策略分析

为什么按结束时间排序是最优的?

  • 结束时间早的活动会给后面留下更多空间
  • 如果选择结束时间晚的,可能会错过多个结束时间早的活动

示例分析

对于样例输入:

4
1 3
4 6
2 5
1 7

程序会这样处理:

  1. 首先将活动按结束时间排序:(1,3), (2,5), (4,6), (1,7)
  2. 选择第一个活动(1,3)
  3. 然后检查剩余活动,发现(4,6)可以参加(因为开始时间4大于3)
  4. 最终可以参加2个活动

注意:当某个活动的开始时间等于另一个活动的结束时间时,这两个活动是可以都参加的

数据类型

输入的时间范围很大(可以到10^9)

  • C++ 中要使用 long long 类型
  • Python的整数类型可以正确处理这个范围的数字

代码实现

C++ 代码

1. 方法一
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

// 定义活动结构体
struct Activity {
   
    long long start, end;
    
    // 重载小于运算符,用于排序
    bool operator < (const Activity& other) const {
   
        return end < other.end;
    }
};

int maxActivities(int n, vector<Activity>& activities) {
   
    // 按照结束时间排序
    sort(activities.begin(), activities.end());
    
    // 记录已选择的活动数量
    int count = 1;
    // 记录上一个选择的活动的结束时间
    long long lastEnd = activities[0].end;
    
    // 遍历所有活动
    for(int i = 1; i < n; i++) {
   
        // 如果当前活动的开始时间大于等于上一个活动的结束时间
        if(activities[i].start >= lastEnd) {
   
            // 选择当前活动
            count++;
            lastEnd = activities[i].end;
        }
    }
    
    return count;
}

int main() {
   
    ios::sync_with_stdio(false);  // 优化输入输出速度
    cin.tie(nullptr);
    
    int n;
    cin >> n;
    
    // 读取所有活动
    vector<Activity> activities(n);
    for(int i = 0; i < n; i++) {
   
        cin >> activities[i].start >> activities[i].end;
    }
    
    // 计算并输出结果
    cout << maxActivities(n, activities) << endl;
    
    return 0;
}
2. 方法二
#include <iostream>
using namespace std;

// 定义活动结构体
struct Activity {
   
    long long start;
    long long end;
};

// 快速排序的分区函数
int partition(Activity arr[], int low, int high) {
   
    Activity pivot = arr[high];
    int i = low - 1;
    
    for(int j = low; j < high; j++) {
   
        if(arr[j].end <= pivot.end) {
   
            i++;
            // 交换元素
            Activity temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp

悦读

道可道,非常道;名可名,非常名。 无名,天地之始,有名,万物之母。 故常无欲,以观其妙,常有欲,以观其徼。 此两者,同出而异名,同谓之玄,玄之又玄,众妙之门。

;