1.题目

​https://pintia.cn/problem-sets/994805342720868352/problems/994805433955368960​​ 给出选课人数和课程数,再分别给出每个人的选课情况,要输出每门课的选课人数和选了该课的所有学生的姓名。

2.思路

和之前的A1039做法一样(只不过是输入和输出倒过来了——所以vector数组的数组元素vector[i]用来装每门课的选课学生,即方便最后输出每门课的学生)。
(1)用二维数组char[N][5]存放输入的姓名,其中char[i]表示第i个姓名,这里不像A1039将学生姓名用hash字符串映射转为数字id,按照一开始给出的学生名字顺序存入name[i],而​​name[course[i][j]]​​​表示i门课的course[i][j]号学生名字——注意j不是该学生编号,​​course[i][j]​​才是学生编号(在printf之前已经对每门课的学生姓名按照字典序用sort排序了)。
以vector数组course[i]存放所有选第i门课的学生编号。
(2)在读入数据时,如果某学生i选择了课程j,则将该学生的编号i存入course[j]。
(3)对每门课i,将course[i]中的学生按姓名字典序从小到大排序最后输出。

3.代码

#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<algorithm>
#include<vector>
using namespace std;
const int maxn=40010; //最大学生人数
const int maxc=2510; //最大课程门数

char name[maxn][5]; //maxn个学生
vector<int> course[maxc]; //course[i]存放第i门课的所有学生编号
//注意上面的是vector数组
bool cmp(int a,int b){
return strcmp(name[a],name[b])<0; //按姓名字典序从小到大排序
}
int main(){
int n,k,c,courseID;
scanf("%d%d",&n,&k); //学生人数及课程数
for(int i=0;i<n;i++){ //对每个学生进行遍历
scanf("%s %d",name[i],&c);
for(int j=0;j<c;j++){ //遍历每个学生的课程编号
scanf("%d",&courseID);
course[courseID].push_back(i); //将学生i加入第courseID门课中
}
}
//输出操作
for(int i=1;i<=k;i++){
printf("%d %d\n",i,course[i].size()); //第i门课的学生数
sort(course[i].begin() , course[i].end(), cmp);//对第i门课的学生排序
for(int j=0; j<course[i].size() ; j++){
printf("%s\n",name[ course[i][j] ]); //输出学生姓名
}
}
system("pause");
return 0;
}

4.注意

(1)使用string有时会超时。
(2)使用vector存放每门课的选课学生编号,可以有效防止所有学生选了所有课程的极端情况导致的空间超限,比直接使用二维数组方便。
(3)strcmp的返回值可能是其他正数或负数,而非一定是-1、0、1。
(4)使用字符串的下标来替代字符串本身进行排序,更快,不会导致大量的字符串移动。