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;
}