1.题目
https://pintia.cn/problem-sets/994805342720868352/problems/994805447855292416 给出N个学生,K门课。
给出每门课号,报名人数N,
给出N个学生的名字,最终要输出每个学生的名字及其选课数,和选得所有课程编号。
2.思路
读取数据时候就将课程编号加入所有选择该课的学生的vector即course[i]
中,最后输出过程:每次for循环先sort排序该学生的选课vector,最后再从小到大输出该学生的选课编号。
(1)学生通过姓名的方式给出;
——如果用STL的map实现姓名与学生编号的映射,最后一组数据过大,导致用map和string会超时,即类似map<string,vector<int>>
或者map<string,set<int>>
写法不行(map慢,其实也可以试下unordered_map
,但是要排序,所以也不行)。所以只能用字符串hash求解,即将二十六进制转成十进制。
int getID(char name[]){//hash函数,将字符串name转成数字
int id=0;
for(int i=0;i<3;i++){
id=id*26+(name[i]-'A');
}
id=id*10+(name[3]-'0');//因为名字的前三位为字母,第4位为数字。
return id;
(2)考生数与课程数的乘积过大,会导致直接开二维数组会有内存超限的问题。
——需要建立vector数组存放不同学生各自选择的所有课程的编号(减少空间消耗)。vector数组的大小至少需要26 * 26 * 26*10(即3个英文字母和1个数字)。
const int M=26*26*26*10+1;//由姓名散列成的数字上界
vector<int> selectCourse[M];//每个学生选择的课程编号装入该vector容器内——selectCourse[i]。
PS:当然这里使用set也是没有问题,即使用set数组或vector数组——每个数组元素就是一个set或vector容器,如果用set就不用对selectCourse[id]进行sort排序了。
(3)数据量过大,用cin和cout会超时,所以用了scanf和printf。
3.代码
#include <iostream>
#include <set>
#include <string.h>
using namespace std;
const int maxn=(26*26*26+1)*10;
set<int> course[maxn];
int getid(char str[]){//hash函数,将字符串name转成数字
int sum=0;
for(int i=0;i<3;i++)
sum=sum*26+(str[i]-'A');
return sum*10+(str[3]-'0');
}
int main(){
int N,K;
char str[10];
scanf("%d%d",&N,&K);//人数及课程数
for(int i=0;i<K;i++){
int courseid,coursenum;
scanf("%d%d",&courseid,&coursenum);//输入课程编号与选课人数
for(int j=0;j<coursenum;j++){
scanf("%s",str);
course[getid(str)].insert(courseid);
}
}
for(int i=0;i<N;i++){//共有N次查询
scanf("%s",str);
printf("%s",str);//不要漏了也要再输出一次姓名
int id=getid(str);
printf(" %d",course[id].size());
for(set<int>::iterator it=course[id].begin();it!=course[id].end();it++)
printf(" %d",*it);
printf("\n");
}
return 0;
}
4.注意
如果不用set而用vector则在查询输出的那个for内,打印之前要行从小到大排序:sort(course[i].begin(),course[i].end())
,sort默认是从小到大排序的。