top1编程
← 返回题目
题解

节省时间

1 条题解

  • 0
    @ 2026-8-6 1:50:12

    P4799 节省时间(入门)

    解题思路

    1. 读懂题目:学生排队找老师答疑,每个学生有一个预估答疑时长。答疑完成时间 = 自己答疑时间 + 等前面同学的时间。题目要求安排排队顺序,让所有学生的"平均完成时间"最少,结果保留两位小数。

    2. 打个比方:就像去医院排队抽血,每个人检查要花不同的时间。如果检查慢的人排在前面,后面所有人都要陪着他等;检查快的人排前面,大家都能早一点完成。所以要把答疑时间短的同学排在前面。

    3. 贪心结论:把答疑时长从小到大排序,就是最优顺序,平均完成时间最小。

    4. 怎么计算平均完成时间:排好队后,第 1 位同学完成时间是自己的时长 t₁;第 2 位同学要等第 1 位,自己再答疑,完成时间是 t₁+t₂;第 3 位是 t₁+t₂+t₃……也就是第 i 位同学的完成时间等于"前 i 位同学答疑时长的总和"(前缀和)。把所有完成时间加起来,除以学生人数,就是平均完成时间。

    5. 看例子:样例答疑时长 3,1,2,6,排序后是 1,2,3,6。完成时间依次是 1、1+2=3、1+2+3=6、1+2+3+6=12,总和是 1+3+6+12=22,22÷4=5.50,和答案一致。

    6. 和哪题类似:这道题和"排队打水""排队接水"是同一类问题,都是让时间短的人先来,从而让平均等待时间最小。

    7. 边界情况:n=1 时平均完成时间就是他自己答疑的时间;n 最多 1000,时长最多 2000,总完成时间可能很大,所以代码里用 long long 累加,防止整数溢出,最后再转成浮点数除以人数。

    参考代码

    // P4799 节省时间:答疑短的排前面,所有人平均完成时间最少
    #include <iostream>
    using namespace std;
    int dur[1005];  // 每位学生的预估答疑时长
    
    int main() {
        int n;
        cin >> n;
        for (int i = 0; i < n; i++) cin >> dur[i];
        // 答疑时长升序排序(冒泡排序,短的排前面)
        for (int i = 0; i < n - 1; i++)
            for (int j = 0; j < n - 1 - i; j++)
                if (dur[j] > dur[j + 1]) { int temp = dur[j]; dur[j] = dur[j + 1]; dur[j + 1] = temp; }
        long long total = 0;  // 所有人完成答疑的时间总和
        long long sum = 0;    // 前面同学的答疑时间总和
        for (int i = 0; i < n; i++) {
            sum += dur[i];     // 等前面同学的时间 + 自己的答疑时间
            total += sum;      // 这就是第 i 位同学的完成时间
        }
        printf("%.2f\n", (double)total / n);
        return 0;
    }
    

    复杂度分析

    冒泡排序的时间是 O(n²),n≤1000,大约一百万次比较,很快;计算前缀和只需要扫一遍数组,是 O(n)。空间上只用了一个长度为 1005 的数组,是 O(n)。整体程序轻巧,轻松通过所有测试点。

    • 1