26. 毕业生录取(1080)
这题的难点在于正确地录取学生到正确学校。开始我理解错了,以为志愿没有优先级,从学校的角度出发,遍历有报名本校的学生,择优录取,但这样发现一个学生可能会先被第二志愿录取,但他实际更愿意去第一志愿,若也可以录取的话,就会出错,而且这样做要遍历m(学校个数)遍学生列表,时间复杂度很大。
实际上,可以仅遍历按成绩排名的学生列表一遍。对每个学生,遍历他的志愿,只有当该学校名额已达到限值,且本学生不与该校上一个录取的名次相同时,才会落选,其它情况一定可以入选。因此我们只需要增加两个额外数组分别存储各个学习当前的录取人数和上一个录取学生名次就可以实现该功能。
本题还有一个注意点是,输出录取情况要按序号输出,因此我又对录取结果每行做了行排序。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<math.h>
using namespace std;struct Students{int ID;int Ge,Gi,avg;int choices[5];int Rank;//int Ad=0;//表示改名学生是否已被录取
}stu[40010];int school[110];
int admis[110]={0},Lrank[110];//各学校实际录取人数,和录取上一名的志排名
int num[110][40010];//存储录取学生序号bool cmp(Students &x,Students &y){if(x.avg!=y.avg) return x.avg>y.avg;else return x.Ge>y.Ge;
}int main(){int n,m,k;scanf("%d%d%d",&n,&m,&k);for(int i=0;i<m;i++){scanf("%d",&school[i]);}for(int i=0;i<n;i++){scanf("%d%d",&stu[i].Ge,&stu[i].Gi);stu[i].avg=(stu[i].Ge+stu[i].Gi)/2;stu[i].ID=i;for(int j=0;j<k;j++){scanf("%d",&stu[i].choices[j]);} }sort(stu,stu+n,cmp);stu[0].Rank=1; for(int i=1;i<n;i++){if(stu[i-1].avg==stu[i].avg && stu[i-1].Ge==stu[i].Ge){stu[i].Rank = stu[i-1].Rank;}else {stu[i].Rank=i+1;} }fill(Lrank,Lrank+m,m);//初始化Lrank数组for(int i=0;i<n;i++){for(int j=0;j<k;j++){int t = stu[i].choices[j];//当前目标学校if(admis[t]>=school[t] && stu[i].Rank>Lrank[t]){//当前目标学校已达到录取名额,且排名不并列,无法录取continue;}else{//能够被录取,录取后break避免重复录取 Lrank[t]= stu[i].Rank;num[t][admis[t]]=stu[i].ID;admis[t]++;break;}}}for(int i=0;i<m;i++){sort(num[i],num[i]+admis[i]);//默认升序排列for(int j=0;j<admis[i];j++){if(j!=0) printf(" ");printf("%d",num[i][j]);}printf("n");} return 0;
}
语法:(1)对二维数组按行排序:sort(num[i],num[i]+admis[i]);不指明比较函数默认升序排列;
(2)小技巧:按平均分排名时,由于成绩个数一样,可以直接比较总分,可以避免平均值带来的误差。
27. 校园停车(1095)
这题整体上还是比较复杂,参考解答后大致完成了。其任务主要有二:
(1)查询任意时刻的停车数量,由于是升序查找,很自然想到把停车记录按时间升序排列。这个问题主要难点在于如何排出无效记录的干扰,因此我们可以先按车牌号排序,筛选出有效的停车记录,保存到另一个备份数组valid中,再对其进行升序查找即可。
(2)输出停车总时间最长的车号码,且可能有多个结果。这个问题要从一个int数据映射到车牌号这一string,因此可以考虑用map建立这一映射关系,在筛选有效停车记录的同时,完成停车时长的计算。
但不知道为什么,按照答案思路做的,第4个测试点老是无法通过,找了几遍也没发现错误原因o(╥﹏╥)o
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
using namespace std;
const int maxn=10010;
struct cars{char id[8]; int time;//按秒存储,便于比较也减少内存使用char status[4];//存储in/out状态
}all[maxn],valid[maxn];map<string,int>Packtime;//建立从车牌到总停车时间的映射bool cmp1(cars x,cars y){//按车牌号、时间的优先级排列if(strcmp(x.id,y.id)!=0) return strcmp(x.id,y.id)<0;else return x.time<y.time;
}bool cmp2(cars x,cars y){//按时间的优先级排列return x.time<y.time;
}int main(){int n,k;scanf("%d%d",&n,&k);int hh,mm,ss;for(int i=0;i<n;i++){scanf("%s%d:%d:%d%s",all[i].id,&hh,&mm,&ss,all[i].status);all[i].time=3600*hh+60*mm+ss;}//先按车牌排列,便于选出有效的停车状态sort(all,all+n,cmp1);int num=0,maxtime=-1;//有效记录数,最大总停留时间for(int i=0;i<n-1;i++){//选出有效的停车记录if(strcmp(all[i].id,all[i].id)==0 && strcmp(all[i].status,"in")==0 && strcmp(all[i+1].status,"out")==0){valid[num++] = all[i];valid[num++] = all[i+1];int temp=all[i+1].time-all[i].time;//本次停车时间unt(all[i].id)==0){//count用于判断map是否存在该键值,第一次生成时停留时间置零Packtime[all[i].id] = 0;}Packtime[all[i].id]+=temp;maxtime=max(maxtime,Packtime[all[i].id]);}}//对有效记录按停车时间排序,便于升序查找sort(valid,valid+num,cmp2);int sum=0,Ti;//当前停车数量,T本次查找时刻int now=0;//由于是升序查找,用now表示当前查询到的位置for(int i=0;i<k;i++){//查询scanf("%d:%d:%d",&hh,&mm,&ss);Ti=3600*hh+mm*60+ss; while(valid[now].time<=Ti && now<num){if(strcmp(valid[now].status,"in")==0) sum++;else sum--;now++;}//已经查询到了本时刻之后的记录,查询完成,可以输出printf("%dn",sum);}//遍历车牌号,找到等于最长停车总时间的车牌号map<string,int>::iterator it;//使用迭代器it遍历访问for(it=Packtime.begin();it!d();it++){if(it->second == maxtime){//it->first是当前映射的键,it->second是当前映射的值printf("%s ",it->first.c_str());//c_str返回当前字符串的首字符地址,//printf输出字符串也是根据其首地址(数组名即为其首地址)}}printf("%02d:%02d:%02dn",maxtime/3600,maxtime%3600/60,maxtime%60);return 0;
}
语法:(1)map的用法,如map,count,迭代器访问等方法等,注意头文件map;
(2) max函数的用法。
散列
以下5题为散列专题,核心思想是用空间换时间,主要是采用直接定址法,将输入内容直接保存到相应数组下标内,从而减少了查询比较的时间。
28. 旧键盘
用双指针分别遍历两个字符串,找到无法匹配的项即是键盘坏掉的键。关键在于避免重复输出和大小写不区分。可以建立辅助数组,根据其值确定该字符是否已被输出过。
#include<iostream>
#include<cstdio>
#include<string>
using namespace std;int num[37]={0};//37个字符对应图中37个数组元素
bool hashFunc(char a){//order变量用于记录出现次序int t; if(a>='A'&&a<='Z'){t=a-'A';if(num[t]==0) {num[t]=1; //第一次发现坏键return false;}}else if(a>='a'&&a<='z'){t=a-'a';if(num[t]==0) {num[t]=1; //第一次发现坏键return false;}}else if(a>='0'&&a<='9'){t=a-'0'+26;if(num[t]==0) {num[t]=1; //第一次发现坏键return false;}}else{if(num[36]==0){num[36]=1; //第一次发现坏键return false;}}return true;
}int main(){string s1,s2;cin>>s1>>s2;int i=0,j=0;while(s1[i]!='