题目链接1080 Graduate Admission
这个题,困扰了我好久,最后发现自己原来是手残,将排序的初值设为了1,应该是0啊!!!!
思路
将所有学生的信息存储下来,包括两个分数ge、gi,以及id,然后按照规则将学生排序。之后遍历已经排好顺序的学生,按照排名来查看当前学生的志愿学校的名额是否已经满了,如果没有满,则将该学生的id加入到该志愿学校的录取名单中,并将该学校的名额减一。如果名额已经满了,则查看该学校最后录取的学生的排名是否与当前学生的排名相同,如果相同,则将当前学生也加入该学校的录取名单。
代码
#include <iostream>
#include <map>
#include <vector>
#include <algorithm>
#include <unordered_map>
using namespace std;
struct applicant{
int id,ge,gi;
applicant(){
}
applicant(int _id,int _ge,int _gi):id(_id),ge(_ge),gi(_gi){
}
bool operator < (const applicant b){
//重载 < 运算符,自定义结构体排序规则
if(this->ge + this->gi != b.ge + b.gi)
return (this->ge + this->gi > b.ge + b.gi);
else
return this->ge > b.ge;
}
};
int n,m,k;
vector<int> quota;//各个学校的名额
vector<applicant> people;//应试者数组
unordered_map<int,int> sequence;//记录学生的排名
unordered_map<int,vector<int>> want;//每个学生的志愿
map<int,vector<int>> ans;//每个学校的录取名单,使用map来存,key有序,本题中无序map也可以
int main()
{
scanf("%d%d%d",&n,&m,&k);
for(int i=0;i<m;i++){
int temp;
scanf("%d",&temp);
quota.push_back(temp);
}
vector<int> cpy(quota);
for(int i=0;i<n;i++){
int id = i;
int ge,gi;
scanf("%d%d",&ge,&gi);
people.push_back(applicant(id,ge,gi));
for(int j=0;j<k;j++){
int temp;
scanf("%d",&temp);
want[id].push_back(temp);
}
}
sort(people.begin(),people.end());
sequence[people[0].id] = 0;//坑了半天!!!!!!!!
for(int i=1;i<n;i++){
if(people[i].ge == people[i-1].ge && people[i].gi == people[i-1].gi)
sequence[people[i].id] = sequence[people[i-1].id];
else sequence[people[i].id] = i;
}
for(int i=0;i<n;i++){
int id = people[i].id;
for(int j=0;j<k;j++){
int cur = want[id][j];
if(!cpy[cur]) continue;//注意,如果该学生想去的学校本来就不招生,则直接跳过该学校
if(quota[cur] > 0){
//如果还有名额,则该学生进入学校录取名单
ans[cur].push_back(id);
quota[cur]--;//名额减少一个
break;
}
else{
//如果该学校收学生但是名额已经满了
int lastindex = ans[cur].size()-1;
int lastid = ans[cur][lastindex];
if(sequence[lastid] == sequence[id]){
//如果该学生的排名与给学校最后录取的学生排名相同
ans[cur].push_back(id);//该学校录取该学生
break;
}
}
}
}
for(int i=0;i<m;i++){
if(!ans[i].size()) printf("\n");
else{
sort(ans[i].begin(),ans[i].end());
for(int j=0;j<ans[i].size();j++){
if(j) printf(" ");
printf("%d",ans[i][j]);
}
printf("\n");
}
}
return 0;
}
坑点:
其实我的思路对了,代码可以通过4个样例,但就是有两个样例通不过,我以为有什么边界数据,但是找不出来。
题目说,排序时,首先看(ge+gi)/2,而ge、gi是用int来存储的,这就是说,如果在结构体applicant中又定义了符合题目要求的一个变量avg,其代表着int 类型的ge、gi的平均数,但是要注意一个问题,就是,因为ge、gi是int型的,如果avg也定义为int类型,则就是整数除法,会只保留整数,举个例子来说,当样例为
2 1 1
1
30 40 0
30 41 0
即有两个应试者,有一个学校收人,每个学生只可以报一个志愿,0号院校的招生名额为1个,这时,看两个应试者的成绩,可以算出来,两人的avg都是35,再看ge成绩,又相同,按照规则这种情况下,0号学校会将两人全部录取。但是其实两人的总分是不同的,只是因为是整数除法,使得得数只保留了整数部分。
而如果,将avg类型修改为double型,又会涉及浮点数比较,无论是在结构体中重载小于运算符还是在遍历数据时,对比当前应试者的avg与目标院校的上一个录取者的avg进行比较时,都涉及浮点数的比较,实在是容易出错。所以,最好是不要计算出avg,而直接使用ge+gi来进行对比。
所以说pat上的数据计算平均数然后进行比较大小的操作时,可以直接使用和来进行比较。
另外,我坑在了一个点上,即排序好的应试者的排名不能取为1,而应该取为0,否则,当people[1]的各项成绩与people[0]的不同时,按照我的写法,这两个数据的排名竟然相同,在这个点上坑了半天。好久没有做排序题了。