算法笔记-排序
A1025
题意:n个考点,k个测试人员,给了学号,和成绩,求考点的排名,和总排名,并按照总排名作为一级输出,学号排名作为二级输出。
思路:结构体存储学生的信息,然后,输入完一个考点的信息,排一次序,如果和前面的成绩相同,则排名相同,如果不同则为下标加一,最后所有的都输入完了,再进行最终排序。
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
struct STD{
char num[20];//放学号
int res;//成绩
int lr;//考点排名
int fr;//最终排名
int ln;//考场号
}stdu[30100];
bool cmp(STD a,STD b){
if(a.res!=b.res)return a.res>b.res;//成绩不同成绩高的在前面
else{//成绩相同学号小的在前面
return strcmp(a.num,b.num)<0;
}
}
int main(){
int n,k;//n个考点,每个考点k个考生
scanf("%d",&n);
int count=0;//记录总人数,方便找到每输入一个结点的范围
for(int i=1;i<=n;i++){
scanf("%d",&k);
for(int j=0;j<k;j++){
scanf("%s %d",stdu[count].num,&stdu[count].res);
getchar();
stdu[count].ln=i;
count++;
}
sort(stdu+count-k,stdu+count,cmp);//sort是左闭右开区间
stdu[count-k].lr=1;
for(int j=count-k+1;j<count;j++){
if(stdu[j].res==stdu[j-1].res)stdu[j].lr=stdu[j-1].lr;
else stdu[j].lr=j-(count-k)+1;
}
}
sort(stdu,stdu+count,cmp);
stdu[0].fr=1;
for(int i=1;i<count;i++){
if(stdu[i].res==stdu[i-1].res)stdu[i].fr=stdu[i-1].fr;
else stdu[i].fr=i+1;
}
printf("%d\n",count);
for(int i=0;i<count;i++){
printf("%s %d %d %d\n",stdu[i].num,stdu[i].fr,stdu[i].ln,stdu[i].lr);
}
system("pause");
return 0;
}
A1062
题意:n个人,l是最低线,必须两个都达标,h是优秀线,两个都超过,是第一类,根据分数排,第二类是,德高与H,但是能力不足,第三类是两个都不足,但是德高,第四类是其他,但合格,第五类是不合格。
思路:我本来想先把每个人拆分成五个vector,然后再进行排序,发现不是最好的办法,但是,答案和我差不多,答案没有给他们分成五个vector,只用了一个tag就解决了,给每个人分了组,分组号小的在前面,分组的方式,因为是if-else结构,如果有最麻烦的就把最麻烦的放在最后有else,其他的平均分。
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
struct STD{
char id[10];//学号
int tag,vir,tal,sum;//tag标记他是那个类的
}stdu[100100];
int Charge(STD a,int H,int L){
if(a.vir<L||a.tal<L)return 5;
else if(a.vir>=H&&a.tal>=H)return 1;
else if(a.vir>=H&&a.tal<H)return 2;
else if(a.vir>=a.tal)return 3;
else return 4;
}
bool cmp(STD a,STD b){
if(a.tag!=b.tag)return a.tag<b.tag;//小的在前面
else if(a.sum!=b.sum)return a.sum>b.sum;
else if(a.vir!=b.vir)return a.vir>b.vir;
else return strcmp(a.id,b.id)<0;//大的在前面,这个变了
}
int main(){
int n,H,L;
scanf("%d%d%d",&n,&L,&H);
int count=0;
for(int i=0;i<n;i++){
scanf("%s%d%d",stdu[i].id,&stdu[i].vir,&stdu[i].tal);
stdu[i].sum=stdu[i].vir+stdu[i].tal;
stdu[i].tag=Charge(stdu[i],H,L);
if(stdu[i].tag!=5)count++;
}
sort(stdu,stdu+n,cmp);
printf("%d\n",count);
for(int i=0;i<count;i++){
printf("%s %d %d\n",stdu[i].id,stdu[i].vir,stdu[i].tal);
}
system("pause");
return 0;
}
A1012
题意:CME三科,再加一个平均数,N个人M个查成绩的,输入六位ID,然后是三个整数分别表示CME。求他们的最好的排名,和该科的代号,给了ACME这样的优先级,如果分数相同就按优先级高的先排名。
思路:这个成绩排序,只输出内部排名(就是对应上一个科目的考点排名),不输出外部排名,上一个是每一个考点输入完了,排一遍。这个只能等所有的都输入完了,排三遍,然后给到他们的单科排名,然后遍历一遍输出他们的最好排名,能不能给排名的时候判断一下然后更新的呢,也可以尝试,但是这里注意排名四次就得是四个排名函数,所以重复率太高,所以就得让他们跟数字建立起联系,这样的话就可以只写一个cmp就行了,所以就是吧他们的成绩都放进一个数组里,然后拍第几个就把第几个成绩送过去排序,因为存储完了,要输入学号查找数值,所以要用哈希表,如果直接都存到结构体里面去,那么没来一个人都要输出一遍
注意:
- for循环改变了增加的量,所有的都要变,不能只改变一个
- sort函数的变量传递,可以放在外面
#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;
struct STD{
int name;
int grad[5];//0123,3个代号表示排的是第几组的成绩
int R[5];//每个的排名
}stdu[5000];
int RK[1000100][4]={0};//因为后来要输入学号查找数值,所以用哈希表,赋值为零的原因是可以判断是否出现过
int now;//now放不了cmp的里面,但是可以放在外面啊,一样可以传递啊
bool cmp1(STD a,STD b){//为了排单科成绩
return a.grad[now]>b.grad[now];//成绩高的放在前面
}
char T[4]={'A','C','M','E'};
void Rank(STD stdu[],int len,int i){
stdu[0].R[i]=1;
RK[stdu[0].name][i]=stdu[0].R[i];
for(int j=1;j<len;j++){
if(stdu[j].grad[i]==stdu[j-1].grad[i]){
stdu[j].R[i]=stdu[j-1].R[i];
}
else {
stdu[j].R[i]=j+1;
}
RK[stdu[j].name][i]=stdu[j].R[i];
}
}
double round(double r)
{
return (r > 0.0) ? floor(r + 0.5) : ceil(r - 0.5);
}
int main(){
int n,m;
scanf("%d%d",&n,&m);
for(int i=0;i<n;i++){
scanf("%d %d %d %d",&stdu[i].name,&stdu[i].grad[1],&stdu[i].grad[2],&stdu[i].grad[3]);
stdu[i].grad[0]= round((stdu[i].grad[1]+stdu[i].grad[2]+stdu[i].grad[3])/3.0)+0.5;
}
for(now=0;now<4;now++){
sort(stdu,stdu+n,cmp1);
Rank(stdu,n,now);
}
for(int i=0;i<m;i++){//判断输出
int cn;//查询学号
scanf("%d",&cn);
if(RK[cn][0]==0)printf("N/A\n");
else{
char bt='A';//最好的课程
int br=RK[cn][0];//最好的排名
for(int j=0;j<4;j++){
if(br>RK[cn][j]){
br=RK[cn][j];
bt=T[j];
}
}
printf("%d %c\n",br,bt);
}
}
return 0;
}
A1016
题意:24个小时,每个小时的费用不一样,给了不同的通话记录,先找到有效的通话记录然后,输出每个人的账单
思路:先进行排序,按照时间排序,找到相邻的online和offline,才是有效的记录,然后遍历计时间,算费用,当换了姓名以后输出总费用,计费是这个题的核心,采用跳动计时间法,但应注意,一天是24小时,不是60个小时,然后,每当60转0的时候,是不跳动时间的,所以当时所有的时间计数都要减一,然后是,当这种变量多的时候,每个变量每次循环之前都要赋初值,所以定义变量的时候,就要写进去,要不然,很容易忘了,就算不忘,也不知道加在哪里。
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
#include<iostream>
using namespace std;
struct NODE{
string name;
int t[5];
int tag;//1表示online,0表示offline
}node[1010];
int charg[25];//记录每个小时的资费
bool cmp(NODE a,NODE b){
if(a.name!=b.name)return a.name<b.name;
for(int i=0;i<4;i++){
if(a.t[i]!=b.t[i])return a.t[i]<b.t[i];
}
}
int main(){
for(int i=0;i<24;i++){//输入资费
scanf("%d",&charg[i]);
}
int n;//n条记录
scanf("%d",&n);
getchar();
for(int i=0;i<n;i++){//输入数据
string t;
cin>>node[i].name;
scanf("%d:%d:%d:%d",&node[i].t[0],&node[i].t[1],&node[i].t[2],&node[i].t[3]);
cin>>t;
if(t=="on-line")node[i].tag=1;
else node[i].tag=0;
}
sort(node,node+n,cmp);//排序
int needprint=0;
float lc=0,alc=0;//lc是每条的计费,alc是总计费
int c1=0,c2=0;//c1计费用,c2输出时常用
for(int i=0;i<n;i++){//遍历,找合格的,计费
if(node[i].name==node[i+1].name){//名字一样,继续找,找到了一个needprint加一,然后,总费用加一
if(node[i].tag==1&&node[i+1].tag==0){//前后连续的on,off,说明是有效的
int d,h,m;
for(d=node[i].t[1],h=node[i].t[2],m=node[i].t[3],c1=0,c2=0,lc=0;
d!=node[i+1].t[1]|| h!=node[i+1].t[2]|| m!=node[i+1].t[3];
m++,c1++,c2++){
if(m==60){//每到了新的一小时,要计费了
lc+=(c1*charg[h])/100.0;
c1=-1;
m=-1;
h++;
c2--;
}
if(h==24){
h=0;
d++;
}
}
if(c1!=0){//说明还有一些分钟没有计费
lc+=(c1*charg[h])/100.0;
}
needprint++;
if(needprint==1){
cout<<node[i].name<<" ";
printf("%02d\n",node[i].t[0]);
}
alc+=lc;//相邻两条记录判断完了
printf("%02d:%02d:%02d %02d:%02d:%02d %d $%.2f\n",node[i].t[1],node[i].t[2],node[i].t[3],node[i+1].t[1],node[i+1].t[2],node[i+1].t[3],c2,lc);
}
}
else{//名字不一样,该换人了,如果上一个人有有效记录的话,就输出计费
if(needprint>0){
printf("Total amount: $%.2f\n",alc);
alc=0;
needprint=0;
}
}
}
return 0;
}
A1028
题意:excel表格排序功能再现。
思路:switch-case走起即可
#include<cstdio>
#include<string>
#include<iostream>
#include<algorithm>
using namespace std;
struct STD{
int ID;
string name;
int grade;
}stdu[100100];
bool cmp1(STD a,STD b){
return a.ID<b.ID;
}
bool cmp2(STD a,STD b){
if(a.name!=b.name)return a.name<b.name;
else return a.ID<b.ID;
}
bool cmp3(STD a,STD b){
if(a.grade!=b.grade)return a.grade<b.grade;
else return a.ID<b.ID;
}
int main(){
int n,c;
scanf("%d%d",&n,&c);
for(int i=0;i<n;i++){
scanf("%d",&stdu[i].ID);
cin>>stdu[i].name;
scanf("%d",&stdu[i].grade);
}
switch (c){
case 1:sort(stdu,stdu+n,cmp1);break;
case 2:sort(stdu,stdu+n,cmp2);break;
case 3:sort(stdu,stdu+n,cmp3);break;
}
for(int i=0;i<n;i++){
printf("%06d ",stdu[i].ID);
cout<<stdu[i].name<<" ";
printf("%d\n",stdu[i].grade);
}
return 0;
}
A1055
题意:给了一堆数据,然后,挑选出符合年龄区间的m个最富有的人
思路:这里既要考虑年龄,又得考虑钱财,两个都要满足条件才可以,我想到的办法,肯定是,先输入进去,一个数组进行存放着,然后遍历,看是否满足区间,按财富排列下标。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<vector>
#include<cstring>
using namespace std;
struct MAN{
string name;
int age;
int w;//钱
}man[100100],valid[100100];//此处的分号别忘了打,否则后面就都错了
//在各个年龄段都是前一百名的先挑出来,然后再整别的,要不然会超时
int AGE[210]={0};
bool cmp(MAN a,MAN b){
if(a.w!=b.w) return a.w>b.w;
else if(a.age!=b.age)return a.age<b.age;
else return a.name<b.name;
}
int main(){
int n,k;
scanf("%d%d",&n,&k);
for(int i=0;i<n;i++){
cin>>man[i].name;
scanf("%d%d",&man[i].age,&man[i].w);
}
sort(man,man+n,cmp);
int validnum=0;//存放到valid数组中的人数
for(int i=0;i<n;i++){
if(AGE[man[i].age]<=100){//用年龄数组来计数,看看是不是到了100个人
AGE[man[i].age]++;
valid[validnum++]=man[i];
}
}
int m,l,r;//m是指的前几名,lr为左右边界
for(int i=1;i<=k;i++){//输出函数
scanf("%d %d %d",&m,&l,&r);
printf("Case #%d:\n",i);
int count=0;//count是记录输出了几个人用的
for(int j=0;j<validnum&&count<m;j++){
if(valid[j].age>=l&&valid[j].age<=r){
cout<<valid[j].name<<" ";
printf("%d %d\n",valid[j].age,valid[j].w);
count++;
}
}
if(count==0)printf("None\n");
}
system("pause");
return 0;
}
A1075
题意:N个人,K个问题,M次提交,每题分值,五位数的ID,题号,得分顺次给出,输出排名,学号,成绩,注意如果没有提交则用-表示,如果提交了没通过编译,分数是0分,但是输入的时候用-1表示
思路:每个人一个成绩数组,先初始化为-1,然后输入,当输入的是-1的时候,变为0,每输入一个和标准成绩比对,看看如果是满分,就把满分记录加1,最前面放总成绩,最后面放满分记录。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
#include<iostream>
#include<vector>
using namespace std;
struct STD{
int ID,total,tag;
int grade[6];
int v;//表示是否编译成功
}stdu[10010];
bool cmp(STD a,STD b){
if(a.total!=b.total)return a.total>b.total;//总成绩高的在前面
else if(a.tag!=b.tag)return a.tag>b.tag;//满分次数多的在前面
else return a.ID<b.ID;
}
int p[7];//放每个题的分值
int main(){
int n,k,m;//n个人,k个问题,m个提交
scanf("%d%d%d",&n,&k,&m);
for(int i=1;i<=k;i++){
scanf("%d",&p[i]);
}
for(int i=1;i<=n;i++){//先赋初值
stdu[i].ID=i;
stdu[i].tag=stdu[i].total=stdu[i].v=0;
memset(stdu[i].grade,-1,sizeof(stdu[i].grade));
}
for(int i=0;i<m;i++){
int id,pro,g;
scanf("%d%d%d",&id,&pro,&g);
if(g==-1&&g>=stdu[id].grade[pro]){
if(stdu[id].grade[pro]==-1)stdu[id].grade[pro]=0;//说明是第一次输入
stdu[id].total+=g-stdu[id].grade[pro];//上述三种情况都需要更新数组,所以,一起整。
stdu[id].grade[pro]=g;
}
if(g>stdu[id].grade[pro]){//只有新来的数>=原来里面的数,才会更新
if(g==-1)g=0;//如果是-1,表明编译没成功,让他的分数变成0,方便后面
if(g==p[pro]){//如果是满分,说明是有效输入,标签变成1,满分标签自加
stdu[id].tag++;
stdu[id].v=1;
}
else stdu[id].v=1;//如果在-1和满分之间的数也是有效输入
if(stdu[id].grade[pro]==-1)stdu[id].grade[pro]=0;//说明是第一次输入
stdu[id].total+=g-stdu[id].grade[pro];//上述三种情况都需要更新数组,所以,一起整。
stdu[id].grade[pro]=g;
}
}
sort(stdu+1,stdu+n+1,cmp);//排序,左闭右开区间
int r=1;
for(int i=1;i<=n&&stdu[i].v==true;i++){//这种直接输出的不需要赋值,碰到不同的再更新rank
if(i>1&&stdu[i].total!=stdu[i-1].total){
r=i;
}
printf("%d %05d %d",r,stdu[i].ID,stdu[i].total);
for(int j=1;j<=k;j++){
if(stdu[i].grade[j]==-1){
printf(" -");
}
else {
printf(" %d",stdu[i].grade[j]);
}
}
printf("\n");
}
system("pause");
return 0;
}
A1083
题意:给了记录,然后输出在一个区间的排名情况,n个记录
思路:答案比我简单很多,因为他不单拿出来,只是遍历一遍有就输出,没有就跳过
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
struct STD{
char name[20];
char id[20];
int grade;
}stdu[1000100];
int cmp(int a,int b){
return stdu[a].grade>stdu[b].grade;
}
int main(){
int n;
scanf("%d",&n);
for(int i=0;i<n;i++){
scanf("%s %s %d",stdu[i].name,stdu[i].id,&stdu[i].grade);
}
int grade1,grade2;
vector<int> res;
scanf("%d%d",&grade1,&grade2);
for(int i=0;i<n;i++){
if(stdu[i].grade>=grade1&&stdu[i].grade<=grade2)
res.push_back(i);
}
if(res.size()==0)printf("NONE");
else {
sort(res.begin(),res.end(),cmp);
for(int i=0;i<res.size();i++){
printf("%s %s\n",stdu[res[i]].name,stdu[res[i]].id);
}
}
system("pause");
return 0;
}
A1080
题意:打了一遍然后被删除了,不打了凭记忆吧
思路:先按照排名给他们每个人整一个排名,然后再次循环,内层循环每个人的志愿,检测有没有配额满了,总体比答案的代码优秀一点
心得:
- 要一条条的排除问题,最终确定问题的板块,和问题的范围,逐渐缩小,挨个数据试一下。
- 如果有一些值,监视没出来,找到他赋值的地方,可能错了。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
struct STD{
int GE,GI;
int c[7];//第一二三志愿
int A;
int rank;
int num;
}stdu[40100];
int r[40100];//用来放排名
bool cmp(STD a,STD b){
if(a.A!=b.A)return a.A>b.A;
else return a.GE>b.GE;
}
int qua[150];
vector<int> res[150];
int main(){
int n,k,m;
scanf("%d%d%d",&n,&m,&k);
for(int i=0;i<m;i++){//m个学校
scanf("%d",&qua[i]);
}
for(int i=0;i<n;i++){//n个人
scanf("%d%d",&stdu[i].GE,&stdu[i].GI);
for(int j=0;j<k;j++){
scanf("%d",&stdu[i].c[j]);
}
stdu[i].A=(stdu[i].GE+stdu[i].GI);
stdu[i].num=i;
}
sort(stdu,stdu+n,cmp);
stdu[0].rank=1;
r[stdu[0].num]=stdu[0].rank;
for(int i=1;i<n;i++){//给他们整个排名
if(stdu[i].A==stdu[i-1].A&&stdu[i].GE==stdu[i-1].GE)
stdu[i].rank=stdu[i-1].rank;
else stdu[i].rank=i+1;
r[stdu[i].num]=stdu[i].rank;
}
for(int i=0;i<n;i++){//按排序录取
for(int j=0;j<k;j++){//k个志愿
if(qua[stdu[i].c[j]]>0||(stdu[i].rank==r[*(--res[stdu[i].c[j]].end())])){
qua[stdu[i].c[j]]--;
res[stdu[i].c[j]].push_back(stdu[i].num);
break;
}
}
}
for(int i=0;i<m;i++){//m个学校的录取情况
if(res[i].size()==0)printf("\n");
else {
sort(res[i].begin(),res[i].end());
printf("%d",res[i][0]);
for(int j=1;j<res[i].size();j++){//输出该学校的录取情况
printf(" %d",res[i][j]);
}
printf("\n");
}
}
system("pause");
return 0;
}
A1095
题意:浙江大学有8个校园,很多大门,每个大门都收集了很多的出入数据和穿过校门的车的车牌号。现在给了信息,然后让你求在特定的点,在校园里停车的数量,和在一天的最后,找到停车时间最长的车
每一辆车有两个正数 N104,是记录的数量,K8104,是查询的数量,然后n行数据,每一行是 车牌号 时间 出入 ,车牌是7位数字英文的混合,时间是小时分钟秒,出入是 in out,说明:所有的时间都是在一天以内的,每一个车都有出入的记录,任何的只出不进,只进不出的都不作为有效数据。保证至少有一辆车满足要求,没有同一时间又进又出的车辆时间是用24小时制,k行查询,每一行给了一个时间点并且时间点是从小到大排列的
输出每一条查询的时候,先输出一个此时间点的总数量,然后最后一行输出停车时间最长的车,和对应的时间,如果不是唯一的则按字母序排列。并且在一行内输出
思路:这个题有这样几个问题需要解决
1.每辆车怎么存储:进出时间,车牌号,时间跨度。每辆车一个结构体,然后整一个数组
2.每辆车的时间跨度怎么计算,可以采用循环的方式,一秒秒的跳动,然后所有进来的车辆时间都加一
也可以每一辆车单独去算一下
3.怎么找某个时间点有哪些车,如果每个时间点都是一个散列的话,一天是60*60*24=86400
,没有超出可行。
4.所以,整体的思路为:来一辆车,边放时间数组,边记录时间跨度,查询的时候,直接转化为秒数即可。最终时间跨度最长的汽车,可以循环一边输出
在这里插入代码片