《算法竞赛进阶指南》——电影

一.题目

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

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

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

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

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

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

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

请你帮忙选择一部电影,可以让观影很开心的人最多。
如果有多部电影满足条件,则在这些电影中挑选观影比较开心的人最多的那一部。

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

第二行输入 n 个整数 a1,a2…an,其中 ai 表示第 i 个科学家懂得的语言的编号。

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

第四行输入 m 个整数 b1,b2…bm,其中 bi 表示第 i 部电影的语音采用的语言的编号。

第五行输入 m 个整数 c1,c2…cm,其中 ci 表示第 i 部电影的字幕采用的语言的编号。

请注意对于同一部电影来说,bi≠ci。

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

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

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

数据范围
1≤n,m≤200000,
1≤ai,bi,ci≤1e9

二.题目分析

题目虽然很长,但是意思应该很清楚…就是说在给出的n个科学家中,每一个科学家会一种语言,那我们就是要在这么多电影中,选出其语音语言对应的科学家的数量最多

但是存在以下三种情况:
a.假设语言ai会的科学家最多,但是有多部电影语音使用的都是ai。
b.语言ai和语言aj会的科学家同时为最大。
c.语言ai和语言aj会的科学家同时为最大,并且有多部电影的语言使用语言ai或aj。

对于情况a,假设电影bi和bj使用的语音都是ai,这时候我们就要比较电影bi和bj使用字幕的语言对应会的科学家数量。

对于情况b,与情况a类似,比较对应的电影bi,bj使用字幕的语言对应会的科学家的数量。

对于情况c,有多部电影使用语言ai,我们就要求出其中使用字幕对应会科学家数量最多的电影bi,同样求出bj,bi再与bj比较。

因而,我们代码要解决以下问题

1.求出科学家使用最多的一种或多种语言ai(ai,aj……)

2.求出是否有电影使用语言ai,若有,是哪个电影或哪些电影?

3.若有多部电影使用语言ai,如何比较电影字幕使用的语言ci,并选择语言ci会的科学家人数最多的一部电影?

问题1

对于问题1,这个很好办,虽然该题目被归类在排序算法的目录下,但是当我们用vector容器存储科学家会的语言时,用sort排序即可,就算题目不能使用sort排序,我们也可以手写一个快排。这样我们就可以求出同一种语言对应的人数,再与当前最大人数比较,若会大于等于当前值,再进入步骤2,3.

首先,我们用容器a来存储科学家使用的语言:vector a,再进行排序:sort(a.begin(),a.end())

然后,我们定义maxv来存储某个语言对应会的科学家的人数。之后我们遍历整个容器,初始时maxv=0,用x来记录当前的语言,while循环直到元素不等于x时,求出该语言对应的maxv的值。

for(int i=0;i<=a.size()-1;;){
	maxv=0;//遍历新的一个值,将其数量先定义为0
	int x=t;//记录当前新的值,因为t还要去遍历其他元素
	while(a[i]==x){//遍历的元素等于x一直循环
		max++;
		i++;
	}
}

这样,每一个语言对应会的科学家的人数就求出来了。

问题2

对于问题2,我们已经求出了语言ai,我们如何求出其对应的电影编号?

首先我们考虑如何存储这一份信息,我们注意到输入的格式是按照电影编号是,我们可以知道电影i使用的语言编号x。

a.若我们用vector来存储:vector[i]=x,那显然我们知道x的情况下,只能用查找算法去求i的值,很花费时间的。但是如果反一下:vector[x]=i,也不可以,因为语言x可能对应很多的电影,那到这里也很容易想到,可不可以用链表来存储?但需要注意的是,由于语言的数量ai可以达到1e9,我们手写链表肯定是不行的,所以其实我们还是用容器来存储,不过自己去定义一个链表的结构体

vecot还是无法实现的,因为你容器插入只可以通过mov_lang.push_back(),因而语言x对应的一条链表不会是mov_lang[x]

b.若我们用set来存储:那你就得同时存储电影的序号和语言{i,x},可是你在只知道语言的情况下,根本没用办法用find()查找。

c.若我们用hash[]哈希表来存储;看起来似乎可以,给我一个电影编号,我找到它在Hash表中的位置,然后把对应的整个链表遍历一下。但是注意,你N取200017(大于200000)的的一个质数,你数据比较小的时候没有问题,但是一旦语言的数量超过200000,那就会有不同的语言对200000取余是同一个数,这样不同的语言就在同一个Hash表的位置当中了!!!所以会造成你可以过很多组数据,但是数据量大的几组过不了,然后会一边觉得自己的算法没有错却又不知道哪里错了(嗯…就是我,代价是30块哈哈~)

d.若我们用map来存储,这个时候就没有问题了,甚至不需要用链表,直接用一个数组:unordered_map> mov_lang,这样,当输入第i个语言使用与语音语言是x时,只需要执行操作:mov_lang[x].push_back(i)而给定我们x时,判断是否有电影与之对应:if(t!=mov_lang.end()),遍历所有的电影:for(auto T:mov_lang[x])

问题3

对于问题3,基于问题2,我们已经得到了电影的编号T,事实上,编号为T的电影使用的字幕的语言为:mov_sub[T-1],(用容器vector存储即可)。那么我们现在要用语言来找科学家的人数,由于语言是唯一的,所以这个好办,我们定义一个unordered_map sci ,在存储科学家会的语言时,输入语言x,我们只需要sci[x]++,这样sci[x] 就是语言x使用的人数了。

那下来我们实现了,给定语言,我们选好电影,也知道电影的字幕语言,它对应科学家的人数,接下来就是比较的问题。

首先我们定义Max={{电影编号,电影语言科学家对应人数},电影字幕对应科学家人数},这样,在问题1中我们求出了当前这个语言的人数maxv,进行比较:if(maxv>Max.first.second)或者if(maxv==Max.first.second)作为依据。

由问题2,我们得到了电影编号T,这里就必须区分一下,大于和等于的区别。

a.对于if(maxv>Max.first.second)我们一定要选一个电影出来(如果有对应的电影的话),并且我们要在这些电影中选择电影字幕对应科学家人数最多的电影,因而我们定义tmp=0来表示这些电影中,ci值最大的电影。

for(auto T:mov_lang[x]){
	if(sub[T-1]>=tmp){
		Max={{T,maxv},sub[T-1]}};
		tmp=sub[T-1];
	}
}

b.对于if(maxv==Max.first.second),我们只有它的ci的值大于当前Max.second的值,才要进行更换,因而不用设置tmp:

for(auto T:mov_lang[x]){
	if(sub[T-1]>Max.second){
		Max={{T,maxv},sub[T-1]}}};
	}
}

临界问题

到此,基本问题都已经解决了,但是这个题目还会存在许多临界问题。
1.sub[T-1]>=tmp不应该写成sub[T-1]>=tmp的,假设语言ai对应的电影bi只有一个,其字幕的语言没有一个科学家会,但是我仍然要选这个电影。
2.存在一种比较极端的情况即为,电影的语音语言没有一种是和科学家的语言是一样的,例如:

1
5
2
2 2
5 5

这样,按照之前的方法,得到的答案就是0,选不出来。但事实上我们应该直接比较ci,求出ci对应的科学家数量最大的电影:

for (int i = 0;i <= m;i++) {
            if (sci[mov_sub[i]] >= maxvv) {
                Max.first.first = i + 1;
                maxvv = sci[mov_sub[i]];
                }
}

但是这样还会有问题喔:另外一种更极端的情况下,电影语音语言,字幕语言和所有科学家的语言完全不一样,那如果按照上面的代码,得到的会是电影编号+1这时候其实随便选一部,但是你要加上:

if (Max.first.first > m && maxvv == 0) {
          Max.first.first = m;
 }

完整代码

#include
#include
#include
#include
#include
using namespace std;
const int N = 200017;
vector<int> a;//科学家的语言..
unordered_map <int, int> sci;
unordered_map <int, vector<int>> mov_lang;
vector<int> mov_sub;//电影的字幕
int n, m;//科学家的数量,电影的数量
int main() {
    cin >> n;
    while (n--) {
        int x;
        scanf("%d", &x);
        a.push_back(x);
        sci[x]++;//第i个语言懂的人数,由于你i不同,所以符合map
    }
    cin >> m;
    //对于电影的语言来说,只要有这个电影就可以,这里记录是哪部电影
    for (int i = 1;i <= m;i++) {
        int x;
        scanf("%d", &x);//第i个电影的语言是x
        mov_lang[x].push_back(i);//电影的编号
    }
    for (int i = 1;i <= m;i++) {
        int x;
        scanf("%d", &x);
        mov_sub.push_back(x);//第i部电影的字幕的语言是编号x
    }
    sort(a.begin(), a.end());
    pair<pair<int, int>, int> Max = { {0,0},0 };
    for (int i = 0;i <= a.size() - 1;) {
        int maxv = 0;//出现最多的次数
        int x = a[i];
        while (a[i] == x) {
            maxv++;
            i++;
        }
        if (maxv > Max.first.second) {//这个语言更多,看有没有对应语言的电影
            auto t = mov_lang.find(x);//有这种语言出现
            if (t != mov_lang.end()) {
                int tmp=0;
                for (auto T : mov_lang[x]) {//这样,编号就是T了
                    if (sci[mov_sub[T - 1]] >= tmp) {
                        Max = { {T,maxv},sci[mov_sub[T - 1]] };
                        tmp=sci[mov_sub[T - 1]];
                    }
                }
            }
        }
        else{
            if(maxv == Max.first.second){
                auto t = mov_lang.find(x);//有这种语言出现
                if (t != mov_lang.end()) {
                    for (auto T : mov_lang[x]) {//这样,编号就是T了
                        if (sci[mov_sub[T - 1]] > Max.second) {
                            Max = { {T,maxv},sci[mov_sub[T - 1]] };
                        }
                    }   
                }
            }
        }
    }
    if (Max.first.first == 0) {//说明没找到一个电影,这时候完全比较字幕的
        int maxvv = 0;
        for (int i = 0;i <= m;i++) {
            if (sci[mov_sub[i]] >= maxvv) {
                Max.first.first = i + 1;
                maxvv = sci[mov_sub[i]];
            }
            if (Max.first.first > m && maxvv == 0) {
                Max.first.first = m;
            }
        }
    }
    cout << Max.first.first;//这样,pos就是编号
}

总结

其实这一题虽然放在排序的目录下,但其实排序算法只是很小的一部分,更多的是map的灵活使用?那确实让自己对set,map的用法更加熟悉了。另外,更一般的做法其实是离散化0.0.

你可能感兴趣的:(刷题笔记,算法)