《算法竞赛进阶指南》0x05 排序-1.电影

AcWing 103. 电影

莫斯科正在举办一个大型国际会议,有 nnn 个来自不同国家的科学家参会。

每个科学家都只懂得一种语言。

为了方便起见,我们把世界上的所有语言用 111 到 10910^9109 之间的整数编号。

在会议结束后,所有的科学家决定一起去看场电影放松一下。

他们去的电影院里一共有 mmm 部电影正在上映,每部电影的语音和字幕都采用不同的语言。

对于观影的科学家来说,如果能听懂电影的语音,他就会很开心;如果能看懂字幕,他就会比较开心;如果全都不懂,他就会不开心。

现在科学家们决定大家看同一场电影。

请你帮忙选择一部电影,可以让观影很开心的人最多。

如果有多部电影满足条件,则在这些电影中挑选观影比较开心的人最多的那一部。

输入格式

第一行输入一个整数 nnn,代表科学家的数量。

第二行输入 nnn 个整数 a1,a2...ana_1,a_2...a_na1,a2...an,其中 aia_iai 表示第 iii 个科学家懂得的语言的编号。

第三行输入一个整数 mmm,代表电影的数量。

第四行输入 mmm 个整数 b1,b2...bmb_1,b_2...b_mb1,b2...bm,其中 bib_ibi 表示第 iii 部电影的语音采用的语言的编号。

第五行输入 mmm 个整数 c1,c2...cmc_1,c_2...c_mc1,c2...cm,其中 cic_ici 表示第 iii 部电影的字幕采用的语言的编号。

请注意对于同一部电影来说,bi≠cib_i \neq c_ibi=ci。

同一行内数字用空格隔开。

输出格式

输出一个整数,代表最终选择的电影的编号。电影编号 1∼m1 \sim m1∼m。

如果答案不唯一,输出任意一个均可。

数据范围

1≤n,m≤2000001 \le n,m \le 2000001≤n,m≤200000,
1≤ai,bi,ci≤1091 \le a_i,b_i,c_i \le 10^91≤ai,bi,ci≤109

输入样例:
复制代码
3
2 3 2
2
3 2
2 3
输出样例:
复制代码
2
参考代码(一)
cpp 复制代码
#include<bits/stdc++.h>  // 万能头文件,包含所有标准库
using namespace std;

/*
问题:电影选择问题
- n个科学家,每人懂一种语言(编号可能很大,1~10^9)
- m部电影,每部电影有语音语言b[i]和字幕语言c[i](两者不同)
- 需要选择一部电影,使得:
  1. 能听懂语音的科学家最多(第一优先级)
  2. 如果平局,则能看懂字幕的科学家最多(第二优先级)
  
核心难点:语言编号范围太大,无法直接开数组统计,需要离散化
*/

const int N = 2e5 + 20;     // N = 200020,略大于最大科学家/电影数
int a[N], b[N], c[N];       // a:科学家语言  b:电影语音  c:电影字幕
int d[3 * N], e[3 * N], lang[3 * N];  
// d:所有语言的原始集合(用于离散化)
// e:去重排序后的语言数组(离散化结果)
// lang:每种语言对应的科学家人数(下标对应e中的位置)

int n, m, t, k;             
// t:d数组的长度(所有语言总数)  
// k:e数组的长度(不同语言的数量)

/**
 * 二分查找函数:在e数组中找到x的位置(下标)
 * e数组是升序排列的不重复语言数组
 * @param x 要查找的语言编号
 * @return x在e数组中的下标(1-based)
 */
int find(int x){
    int left = 1, right = k;      // 在e[1]~e[k]范围内二分查找
    while(left < right){
        int mid = left + right + 1 >> 1;  // 右中位数,避免死循环
        if(e[mid] > x) right = mid - 1;   // mid太大,往左半部分找
        else left = mid;                    // mid <= x,往右半部分找
    }
    return left;  // 返回找到的位置
}

int main(){
    // 加速输入输出
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    
    // ========== 第一步:读取输入,收集所有语言 ==========
    cin >> n;  // 科学家数量
    for(int i = 1; i <= n; i ++){
        cin >> a[i];           // 读取科学家懂的语言
        d[++ t] = a[i];        // 存入d数组,t自增
    }
    
    cin >> m;  // 电影数量
    for(int i = 1; i <= m; i ++){
        cin >> b[i];           // 读取电影语音语言
        d[++ t] = b[i];        // 存入d数组
    }
    for(int i = 1; i <= m; i ++){
        cin >> c[i];           // 读取电影字幕语言
        d[++ t] = c[i];        // 存入d数组
    }
    // 此时d数组中包含了所有出现的语言,长度t = n + 2m
    
    // ========== 第二步:离散化(排序+去重) ==========
    sort(d + 1, d + t + 1);     // 将d数组排序(升序)
    
    for(int i = 1; i <= t; i ++){
        // 如果是第一个元素,或者当前元素与前一个不同
        if(i == 1 || d[i] != d[i - 1]){
            e[++ k] = d[i];     // 加入到去重数组e中
        }
    }
    // 此时e[1]~e[k]包含了所有不同的语言,按升序排列
    // k是不同语言的数量
    
    // ========== 第三步:统计每种语言有多少科学家懂 ==========
    for(int i = 1; i <= n; i ++){
        int pos = find(a[i]);    // 找到科学家语言在e中的下标
        lang[pos] ++;             // 对应语言的人数+1
    }
    // 例如:如果e[3]=5,lang[3]=10,表示编号为5的语言有10个科学家懂
    
    // ========== 第四步:遍历电影,选择最优 ==========
    int ans = 1;        // 答案,初始化为第一部电影
    int num_1 = 0;      // 当前最优电影的听懂语音人数
    int num_2 = 0;      // 当前最优电影的看懂字幕人数
    
    for(int i = 1; i <= m; i ++){
        // 计算第i部电影能听懂语音的人数
        int x = lang[find(b[i])];  // 找到语音语言对应的下标,获取人数
        // 计算第i部电影能看懂字幕的人数
        int y = lang[find(c[i])];  // 找到字幕语言对应的下标,获取人数
        
        // 比较规则:
        // 1. 优先选择听懂语音人数更多的
        // 2. 如果听懂人数相同,选择看懂字幕人数更多的
        if(x > num_1 || (x == num_1 && y > num_2)){
            ans = i;        // 更新答案为当前电影
            num_1 = x;      // 更新最优听懂人数
            num_2 = y;      // 更新最优看懂人数
        }
    }
    
    // ========== 第五步:输出结果 ==========
    cout << ans;  // 输出电影编号(1-based)
    return 0;
}
参考代码(二)
cpp 复制代码
#include<bits/stdc++.h>  // 万能头文件,包含所有标准库
using namespace std;

const int N = 2e5 + 20;  // 定义数组大小,2e5=200000,+20是为了防止边界问题
// N足够大,因为科学家数量n ≤ 200000

int n, m;           // n: 科学家数量, m: 电影数量
int a[N];           // a数组:存储每个科学家懂的语言编号

map<int, int> p;    // map映射:键 = 语言编号,值 = 懂这种语言的科学家数量
                    // 使用map的原因:语言编号最大可达10^9,无法直接用数组下标
                    // map自动处理大范围键值,相当于内置的离散化

/**
 * 结构体cinema:存储每部电影的信息
 * listen: 电影的语音语言
 * watch:  电影的字幕语言
 * 注意:同一部电影中listen != watch
 */
struct cinema{
    int listen;  // 语音语言编号
    int watch;   // 字幕语言编号
} q[N];          // q数组:存储所有电影,q[1]~q[m]

int main(){
    // ========== 第一步:读取科学家信息 ==========
    scanf("%d", &n);  // 读取科学家数量
    
    // 循环读取每个科学家懂的语言
    for(int i = 1; i <= n; i ++){
        scanf("%d", a + i);  // 指针写法,等价于 &a[i]
        // a + i 表示数组a第i个元素的地址
        
        p[a[i]] ++;  // 使用map统计每种语言的人数
        // p[语言编号] 自动增加1
        // 例如:p[2] = 3 表示编号为2的语言有3个科学家懂
        // 如果语言首次出现,map会自动创建该键并初始化为0,然后加1
    }
    
    // ========== 第二步:读取电影信息 ==========
    scanf("%d", &m);  // 读取电影数量
    
    // 读取每部电影的语音语言
    // 注意:输入顺序是:先输入所有电影的语音语言,再输入所有电影的字幕语言
    for(int i = 1; i <= m; i ++)
        scanf("%d", &q[i].listen);  // 存储第i部电影的语音语言
    
    // 读取每部电影的字幕语言
    for(int i = 1; i <= m; i ++)
        scanf("%d", &q[i].watch);   // 存储第i部电影的字幕语言
    
    // ========== 第三步:遍历电影,选择最优 ==========
    int lm = -1;    // 当前最优电影的听懂语音人数(初始化为-1)
    int lw = -1;    // 当前最优电影的看懂字幕人数(初始化为-1)
    int k;          // 最终选择的电影编号(将在循环中被赋值)
    
    // 遍历所有电影,从1到m
    for(int i = 1; i <= m; i ++){
        // 计算第i部电影的相关人数
        
        // lis = 能听懂语音的人数
        // p[q[i].listen] 直接通过map查找:
        // 如果这种语言在map中存在,返回对应的科学家数量
        // 如果不存在,返回0(map的特性,访问不存在的键会返回0)
        int lis = p[q[i].listen];  // 听懂语音的人数
        
        // wat = 能看懂字幕的人数
        int wat = p[q[i].watch];   // 看懂字幕的人数
        
        /**
         * 比较规则(题目要求):
         * 1. 第一优先级:让很开心的人(能听懂语音)尽可能多
         * 2. 第二优先级:如果第一优先级相同,让比较开心的人(能看懂字幕)尽可能多
         * 
         * 判断条件:
         * - 如果当前电影的听懂人数 > 历史最优听懂人数 (lis > lm)
         *   直接选择当前电影
         * - 如果当前电影的听懂人数 == 历史最优听懂人数 (lis == lm)
         *   并且当前电影的看懂人数 > 历史最优看懂人数 (wat > lw)
         *   也选择当前电影
         */
        if(lis > lm || (lis == lm && wat > lw)){
            k = i;      // 更新答案为当前电影编号
            lm = lis;   // 更新历史最优听懂人数
            lw = wat;   // 更新历史最优看懂人数
        }
    }
    
    // ========== 第四步:输出结果 ==========
    printf("%d", k);  // 输出最终选择的电影编号(1-based)
    
    return 0;
}
相关推荐
CoderCodingNo2 小时前
【GESP】C++八级考试大纲知识点梳理 (6) 图论算法:最小生成树与最短路
c++·算法·图论
DeepModel2 小时前
【特征选择】嵌入法(Embedded)
人工智能·python·深度学习·算法
.YM.Z2 小时前
C++入门——缺省参数,函数重载,引用,inline函数,nullptr的介绍和使用
开发语言·c++
今儿敲了吗2 小时前
算法复盘——前缀和
笔记·学习·算法
ulias2123 小时前
智能指针简述
开发语言·c++·算法
阿昭L3 小时前
Windows通用的C/C++工程CMakeLists
c语言·c++·windows·makefile·cmake
寻寻觅觅☆3 小时前
东华OJ-基础题-58-素数表(C++)
开发语言·c++·算法
桦03 小时前
【C++复习】:多态
c++
AI成长日志3 小时前
【强化学习专栏】深度拆解:多智能体强化学习核心理论与工程实践
算法