每日一题——列车乘客最优安排行程(暴力枚举)

问题本质分析

一列火车从第 0 站开到第 n n n 站,有 m m m 个座位, x x x 名乘客,每个乘客给定上下车站点 a i a_i ai b i b_i bi。要求找出一组乘客安排,使得任一时刻乘客总数不超过 m m m,且所有乘客的乘坐站数总和最大化


解题思路

  1. 二进制枚举所有乘客组合(最多 2 x 2^x 2x 种)。
  2. 模拟每种组合
    • 对每一站点,统计乘客数量,确保不超过 m m m
    • 如果合法,就计算该组合的总乘坐站数(即每个乘客的 b i − a i b_i - a_i biai)。
  3. 记录最大值

✅ 输入输出说明

输入

m n x
a1 b1
a2 b2
...
ax bx
  • m m m:座位数
  • n n n:站点数(编号 0 ~ n-1)
  • x x x:乘客数
  • 每一对 ( a i , b i ) (a_i, b_i) (ai,bi) 表示第 i i i 位乘客的上车站和下车站(不含 b i b_i bi

输出

  • 最大总乘坐站数(即最大座位利用数)

C语言版实现

#include 
#include  // 包含字符串处理函数,如memset

// 定义一个结构体表示乘客的行程信息
typedef struct passager {
    int start; // 乘客上车的站点
    int end;   // 乘客下车的站点
} passager;

// 定义一个简单的最大值函数
int max(int a, int b) {
    return (a > b) ? a : b; // 返回a和b中的较大值
}

// 定义最大座位数、最大站点数和最大乘客数
#define max_seat 10
#define max_station 20
#define max_people 10

int main() {
    // 定义一些变量
    int m = 10; // 最大座位数
    int n = 20; // 最大站点数
    int x = 9;  // 最大乘客数
    int valid = 1; // 用于标记当前组合是否有效
    int ans = 0;   // 当前组合的总行程长度
    int result = 0; // 最终结果,即最大有效行程长度

    // 修改变量值,用于测试
    m = 2;  // 修改最大座位数为2
    n = 11; // 修改最大站点数为11
    x = 4;  // 修改最大乘客数为4

    // 定义一个乘客数组,最多支持max_people个乘客
    passager p[max_people];

    // 初始化乘客的行程信息
    p[0].start = 0; p[0].end = 1; // 第一个乘客从站点0到站点1
    p[1].start = 1; p[1].end = 9; // 第二个乘客从站点1到站点9
    p[2].start = 0; p[2].end = 10; // 第三个乘客从站点0到站点10
    p[3].start = 3; p[3].end = 8; // 第四个乘客从站点3到站点8

    // 定义一个数组,用于记录每个站点的乘客数量
    int count[max_station] = {0}; // 初始化所有站点的乘客数量为0

    // 遍历所有可能的乘客组合
    for (int i = 0; i < (1 << x); i++) { // 1<
        ans = 0; // 重置当前组合的总行程长度
        memset(count, 0, sizeof(count)); // 把整个数组的所有字节设置为0,即重置每个站点的乘客数量
        valid = 1; // 假设当前组合是有效的

        // 遍历每个乘客
        for (int j = 0; j < x; j++) {
            // 检查当前乘客是否在当前组合中
            if ((i >> j) & 1) { // 如果第j位为1,表示乘客j在当前组合中
                // 遍历该乘客的行程范围
                for (int k = p[j].start; k < p[j].end; k++) {
                    count[k]++; // 该站点的乘客数量加1
                    if (count[k] > m) { // 如果某个站点的乘客数量超过最大座位数
                        valid = 0; // 标记当前组合无效
                        break; // 退出内层循环
                    }
                }
                if (valid == 0) {
                    break; // 如果当前组合无效,退出外层循环
                } else {
                    ans += p[j].end - p[j].start; // 累加当前乘客的行程长度
                }
            }
        }

        // 如果当前组合有效,更新最大行程长度
        if (valid) {
            result = max(result, ans); // 更新最大行程长度
        }
        printf("%d\n", result); // 打印当前的最大行程长度
    }

    // 打印最终结果
    printf("%d", result);
    return result; // 返回最终结果
}

详细注释解释:

  1. 头文件

    • #include :包含标准输入输出库,用于printf等函数。
    • #include :包含字符串处理函数,如memset,用于初始化数组。
  2. 结构体定义

    • typedef struct passager:定义了一个结构体passager,包含两个成员变量startend,分别表示乘客的上车和下车站点。
  3. 最大值函数

    • int max(int a, int b):定义了一个简单的函数,返回两个整数中的较大值。
  4. 宏定义

    • #define max_seat 10:定义最大座位数。
    • #define max_station 20:定义最大站点数。
    • #define max_people 10:定义最大乘客数。
  5. 变量定义

    • int m:最大座位数。
    • int n:最大站点数。
    • int x:乘客数量。
    • int valid:标记当前乘客组合是否有效。
    • int ans:当前组合的总行程长度。
    • int result:最终结果,即所有有效组合中的最大行程长度。
  6. 乘客行程初始化

    • 定义了一个乘客数组p,并初始化了4个乘客的行程信息。
  7. 站点乘客数量数组

    • 定义了一个数组count,用于记录每个站点的乘客数量,初始化为0。
  8. 遍历所有乘客组合

    • 使用一个循环for (int i = 0; i < (1 << x); i++),通过位运算生成所有可能的乘客组合。1 << x表示2的x次方,即所有乘客的组合数。
  9. 检查每个组合的有效性

    • 对于每个组合,遍历每个乘客,检查其行程是否会导致某个站点的乘客数量超过最大座位数m。如果超过,则标记该组合无效。
  10. 更新最大行程长度

    • 如果当前组合有效,计算该组合的总行程长度,并更新最大行程长度result
  11. 输出结果

    • 在每次迭代中打印当前的最大行程长度,并在程序结束时打印最终结果。

代码逻辑总结:

  • 该程序通过遍历所有可能的乘客组合,检查每个组合是否有效(即是否会导致某个站点的乘客数量超过最大座位数),并计算每个有效组合的总行程长度。
  • 最终输出所有有效组合中的最大行程长度。

时间复杂度分析

  • 枚举所有乘客组合: O ( 2 x ) O(2^x) O(2x)
  • 每种组合模拟乘坐过程:最多 O ( x ⋅ n ) O(x \cdot n) O(xn)
  • 总体复杂度: O ( 2 x ⋅ n ) O(2^x \cdot n) O(2xn),适合 x ≤ 20 x \leq 20 x20

示例

输入:

2 10 3
0 5
2 8
5 10

输出:

15

说明:第 1、2、3 位乘客都能安排,每个人乘坐 5、6、5 站,加起来为 16,但如果超了座位限制,就只能选两人,最优组合就是乘坐站数和为 15。


思维拓展

可以考虑贪心或动态规划的方式进一步优化,但由于本题规模较小,暴力+剪枝方式是最直接有效的方法。

代码讲解

for (int i = 0; i < (1 << x); ++i)
这是用来遍历2x 次,1<x;
这行代码:

  • int count[n]:定义了一个长度为 n 的整型数组,n 必须是一个已定义的整数常量(在C中,不能是变量,除非使用C99变长数组或在GCC扩展中允许)。
  • {0}:这是一种聚合初始化,只给出了第一个元素的初始值,其他未显式初始化的元素会自动初始化为 0。

示例:

const int n = 5;
int count[n] = {0};  // 等价于 int count[n] = {0, 0, 0, 0, 0};
C语言如何清零数组

memset(count, 0, sizeof(count)); // 把整个数组的所有字节设置为 0

华为刷题第一题,直接暴力穷举。确实挺绕思路的。但是一点点进步。拿到一百五十分。

你可能感兴趣的:(学习笔记,算法题,C语言,学习,笔记,数据结构,算法)