[Codeforces 670C ]Cinema

本题思路是用标记数组存储各种语言对应的人数(“满足人数”实质为该语种的人数

),然后通过枚举所有电影来获取音频满足人数最多的电影,如果存在多个满足音频人数最多的电影,则需要求其中字幕满足人数最多的电影。

然而问题是语言的范围过大,无法用数组存储,因此无法实现标记数组

方案是用离散化压缩数组。离散化是利用一组数据中有效数据的大小关系来压缩存储空间的方法。本质是将数组中不存储数据的部分剔除,只存放有效数值。

主要针对数据范围太大(标志是109,无法用数组的下标对应数据的范围。例如标记数组的容量需要对应数据的可能范围。这时候因为数据的个数并不多,可以用数据的相对大小来代表原数据。

如何定义相对大小?我们不妨取数据里的最小值为标准,它的离散化为1,而数据中次小的元素的离散化则为2,…直到数组最大值。离散化原数组的秩对应新数组的映射用于映射的数组称为离散数组。

原数组和新数组的区别在于:1.新数组没有原数组的冗余空间。2.原数组的秩对应原始数据的值;新数组的秩采用原数组数值的相对大小表示。

如何求解离散化?

通常的流程是“排序、去重、标号(映射)”

先排序,得出相对大小顺序,序号恰为下标,虽然这是原序列的相对大小,但不能直接输出排序后的结果,因为我们关心的是各类元素的数量,本题的元素种类是语种,还需要获得种类数及对应数量。对于含有重复元素的序列,去重操作可以得到种类数不含重复元素的序列,则不必去重,因为每种元素出现的次数最多一次。而给原序列标相对序号后,可以通过遍历原序列并对每个元素统计所属类的元素的个数求各类元素的数量。事实上,我们根据对拥有相同种类集的不同的序列求对应该序列的各类元素的数量。

对于求离散化的实现与实例解说,可以参见:https://zhuanlan.zhihu.com/p/85776198

本题的待离散化的数据,即语种来源,实质为3种:人、语音、字幕。人部分显然需要标记出现次数,而电影的语音和字幕是查询项目,也就是查询标记数组中对应的数值。由于查询的语种是原始语种值,需要离散化的映射才能和标记数组的秩的对应。那么对于这些来源的数据总量上限应该是三种语言来源上限的总和,则离散数组的容量应该为三种语言来源上限的总和

为了实现相对大小的最小为1,要建立从下标1开始的语种全集。当然从0也行。

const int maxm=2e5+10;
using namespace std;
int people[maxm],al[maxm],sl[maxm],all[3*maxm];//人、音频、字幕、所有语种集 

	cin>>n;
	for(i=0;i<n;i++)
	{cin>>people[i];
	all[++t]=people[i];//从下标为1开始录入 
	}
	cin>>m;
	for(i=0;i<m;i++)//人与电影数不同,不要忘记改枚举次数
	{cin>>al[i];
	all[++t]=al[i];
	}
	for(i=0;i<m;i++)
	{cin>>sl[i];
	all[++t]=sl[i];
	}
	sort(all+1,all+t+1);//排序区间为[1,t+1) 
	int cur=unique(all+1,all+t+1)-all-1;//unique返回值是去重之后的尾地址(第一个重复元素的地址),需要减去首元素all[1]的地址获得去重后数组的长度。它的时间复杂度为O(N)
	

此时的all数组的无重部分存储各语种的相对大小为它的下标,all的无重部分则为语种集。语种集的基为cur,cur=unique-all的存储第一元素的地址。则如果从0录入,cur=unique(all,all+t)-all;

通过 lower_bound 作为语种到该语种人数的映射lower_bound是 二分查找第一个大于或等于关键字的数字的地址。这个不重要,只要知道它是高效的查找就行。因为本题的数据范围用二分比线性高效。

因为需要多次求人数,需要写成函数。

int query(int num){//二分查找num的位置
    lower_bound(all+1,all+cur+1,num)-all;//由于首地址的秩为0,可以通过减首地址得到该语种的相对大小秩
}

注意是查询到的地址减首地址得到秩。如果待离散数组从0开始记录,也是减首地址。

求各语种的人数,就是枚举所有人,用新数组做标记数组,标记离散值的出现次数。

int sum[maxm*3];
for(i=0;i<n;i++){
		num=query(people[i]); 
		sum[num]++;
	}

接下来对每个电影审查,类似求一组数的最大值对应的下标,这里需要枚举所有电影求所有电影中标记次数最大的电影的下标。但是有优先顺序,先考虑音频,如果找到更大的音频电影的下标,则音频和字幕能满足人数最多的电影的下标要更新。如果音频能满足人数一样多,则求字幕满足人数最多的电影的下标,并更新。最优先的比较中音频和字幕同时更新是因为当前认为该电影是最优选择,应该更新音频和字幕最优电影的下标。否则,如果只更新音频标记,那么求字幕最优时,可能出现音频满足人数不一样多,但音频满足人数少的电影的字幕更优于音频更优的电影,这是题目不想要的,这也是很混乱的比较。因此求出音频满足人数最多的电影后,需要更新音频和字幕最优电影的下标为该电影。

int am=0,sm=0;//记录最大满足人数的电影的音频语种下标,同时勉强满足人数最多的字幕语种下标 
	for(i=0;i<m;i++){
		int x=query(al[i]);//音频的离散值 
		int y=query(sl[i]);//字幕的离散值 
		if(sum[am]<sum[x]){//第i个电影满足人数是否是最大? 用离散值查询 注意关注重点在于是否是最大,如果同大则考虑else,而不是考虑判断同种语言 
			sm=am=x;
		}
		else if(sum[am]==sum[x]&&sum[sm]<sum[y]) //第i个电影勉强满足人数是否是最大? 同上要求数量而不强调种类。 
		{
			sm=y;
		}//两次求最大都要求严格不等号,为了记录满足要求的最小下标的电影 
	}
	 if(sm>am)cout<<sm<<endl;//由于从小到大遍历,如果sm比am更大,则是满足人数和勉强满足人数最多的电影 
	 else cout<<am<<endl;//否则,输出am,即仅考虑满足人数最大而不考虑勉强满足人数的电影
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值