Files
python/TangDou/CSP-J/AcWingTraining/T1/442.md
HuangHai 73b3035577 'commit'
2025-11-20 13:18:00 +08:00

2.3 KiB

排队接水

一、原题

https://www.luogu.com.cn/problem/P1190

P1190 [NOIP 2010 普及组] 接水问题

#include <bits/stdc++.h>
using namespace std;
const int MAX_N = 10010; // 最大人数
const int MAX_M = 110;   // 最大水龙头数
int n, m;                // n: 同学人数, m: 水龙头数量
int w[MAX_N];            // 每位同学的接水量
int taps[MAX_M];         // 每个水龙头的当前结束时间
int main() {
    cin >> n >> m;

    // 模拟接水过程
    for (int i = 0; i < n; i++) {
        cin >> w[i];

        // 找到最先结束的水龙头
        int min_tap = 0;
        for (int j = 1; j < m; j++)
            if (taps[j] < taps[min_tap])
                min_tap = j;
        // 将i号同学的接水量分配给最先结束的水龙头
        taps[min_tap] += w[i];
    }
    // 找到所有水龙头中最大的结束时间
    int result = 0;
    for (int i = 0; i < m; i++)
        result = max(result, taps[i]);

    cout << result << endl;
    return 0;
}

二、变形2【一个水龙头前排队接水】

https://www.acwing.com/problem/content/description/5945/

#include <bits/stdc++.h>
using namespace std;
const int N = 1005;

struct Node {
    int num;
    int sum;
} a[N];
bool cmp(Node x, Node y) {
    if (x.sum == y.sum)
        return x.num < y.num;
    return x.sum < y.sum;
}
int main() {
    int n;
    cin >> n;
    for (int i = 0; i < n; i++) {
        cin >> a[i].sum;
        a[i].num = i + 1;
    }
    sort(a, a + n, cmp);
    double temp = 0.00;
    for (int i = 0; i < n; i++) {
        cout << a[i].num << " ";
        temp += (n - (i + 1)) * a[i].sum;
    }
    cout << endl;
    printf("%.2f", temp / n * 1.00);
    return 0;
}

四、数学证明

首先我们要排的是所有的元素,但是为什么是从小到大呢???

排队总是象征要排序,每个元素在此序列下都要满足条件,也就是从中拿出两个相邻元素同样满足条件:

于是设:

a_i 和 $b_i$且a_i<b_i

那么针对这两个元素:就有两种排列情况:

1.$a_i$排在$b_i$前面那么有总时间:t_1=a_i+a_i+b_i.

2.$b_i$排在$a_i$前面那么有总时间:t_2=b_i+b_i+a_i.

##于是由$a_i<b_i$得出 $t_1<t_2$—》变一下式子—》a_i+a_i+b_i<b_i+b_i+a_i;

再化简不等式得出a_i<b_i

于是得出结论:当$a_i$在$b_i$前面时,时间为最小值

于是反推回总体,两两相较,那么越小的应该越排在前面,以至于总时间越小。