最新文章

这里会显示最新的几篇文章摘要。

鲁的女孩(桶排序)

问题 B: 鲁的女孩

时间限制: 1.000 Sec 内存限制:

题目描述

在银河系的遥远角落,宇宙大帝 Luke 时常举办各种奇异的挑战赛,以此来激发年轻探险者们的潜能。这一次,他邀请了一位聪明过人的小女孩 SK 来到他的星球 Lu3KO5。Luke 准备了一场独特的智力挑战,所有的银河居民都在关注着这场比赛。

为了考验 SK 的智慧,Luke 召唤出了两个神秘的时空袋子,袋子 A 和袋子 B。这两个袋子拥有吸引宇宙中不同数字的能力,能够将这些数字封存在袋子中。比赛共有 n 轮,每一轮,Luke 都会从宇宙的时间长河中提取出两个神秘的数字 a 和 b,分别放入袋子 A 和袋子 B 中。

然后,Luke 向 SK 提出挑战:如果她将袋子 A 中的所有数字与袋子 B 中的所有数字按照时空法则一一配对,她需要找出其中配对后和最大的那一对的最小值。只有得出正确答案,她才能继续接受下一轮的挑战。

SK 思维敏捷,很快就找到了答案。但是为了确保万无一失,她决定在回答 Luke 之前,先找你确认一下她的答案是否正确。现在,银河的命运掌握在你们手中,能否帮助 SK 通过这场挑战呢?

输入

输入第一行为一个整数 n,表示比赛的轮数。

接下来 n 行,每行包含两个整数 a 和 b,分别表示每轮中放入袋子 A 和袋子 B 的神秘数字。

输出

输出 n 个整数,第 i 个整数表示将前 i 个袋子 A 和前 i 个袋子 B 中所有数字一一配对后,和最大的一对的最小值。

样例输入

3
2 8
3 1
1 4

样例输出

10
10
9

提示

样例 解释
对于第一组询问 2+8=10。
对于第二种询问 2+8=10,3+1=4。
对于第三组询问 1+8=9,3+1=4,2+4=6。

对于 30% 的数据,n ≤ 20。
对于 50% 的数据,n ≤ 100。
对于 100% 的数据,\(n ≤ 10^5, 1 ≤ a, b ≤ 100\)

分析

利用题目中 a、b 的取值范围只有 1~100,可以用计数数组来维护袋子 A 和袋子 B 的数字分布,从而避免每轮使用 vector 插入排序带来的 O(n) 操作。整个思路如下:

  1. 用两个大小为 101 的数组 freqA 和 freqB 分别记录袋子 A(升序)和袋子 B(降序)中各数字出现的次数。

  2. 每轮输入一对数字后,直接更新 freqA[a]++ 和 freqB[b]++。

  3. 对于当前 round(即当前总数 count),我们“模拟”排序后的配对。袋子 A 中排序后是:数字 1 重复 freqA[1] 次、数字 2 重复 freqA[2] 次……;袋子 B 中排序后是数字 100,99,…,1,分别重复 freqB 对应次数。

    我们用“模拟归并”的方式,用两个指针 i(从 1 开始扫描 freqA)和 j(从 100 开始扫描 freqB),每次取两侧当前块的数量进行配对,成对的和就是 i+j。累加配对数,直到完成当前 round 数量的配对。记录所有配对中 sum 的最大值即为答案。

  4. 由于每次最多扫描 100 个数字,所以每轮的时间复杂度为 O(101*2) = O(200) 级别,总体 n 轮时间复杂度为 O(n)。

下面给出参考代码(文件路径 a:\codes\b.cpp):

#include <iostream>
#include <algorithm>
using namespace std;

int freqA[101] = {0}; // 袋子 A 的计数(升序排序)
int freqB[101] = {0}; // 袋子 B 的计数(降序排序)

int main(){
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    
    int n, a, b;
    cin >> n;
    
    // 分别记录现有数字个数
    int round = 0;
    while(n--){
        round++;
        cin >> a >> b;
        freqA[a]++;
        freqB[b]++;
        
        // 通过模拟配对来获取最大的一对和
        int cnt = 0;
        int ans = 0;
        int i = 1, j = 100;
        // 为防止破坏原数组,使用临时变量
        int tempA[101], tempB[101];
        for (int v = 1; v <= 100; v++){
            tempA[v] = freqA[v];
            tempB[v] = freqB[v];
        }
        while(cnt < round && i <= 100 && j >= 1){
            while(i <= 100 && tempA[i] == 0) i++;
            while(j >= 1 && tempB[j] == 0) j--;
            if(i > 100 || j < 1) break;
            
            int available = min(tempA[i], tempB[j]);
            // 注意最后一组可能只用部分数量即可配完
            if(cnt + available > round){
                available = round - cnt;
            }
            ans = max(ans, i + j);
            cnt += available;
            tempA[i] -= available;
            tempB[j] -= available;
        }
        cout << ans << "\n";
    }
    return 0;
}

这样,每轮只需要 O(200) 步,避免了原来使用 vector 插入和局部更新 dp 的高时间复杂度,从而可以高效处理 n 达到 10⁵ 的数据。

posted @ 2025-03-23 19:07  bakul  阅读(85)  评论(0)    收藏  举报