PAT乙級 1080 MOOC期終成績 (25 分)
對於在中國大學MOOC(http://www.icourse163.org/ )學習“資料結構”課程的學生,想要獲得一張合格證書,必須首先獲得不少於200分的線上程式設計作業分,然後總評獲得不少於60分(滿分100)。總評成績的計算公式為 G=(Gmid−term×40%+Gfinal×60%),如果 Gmid−term>Gfinal;否則總評 G 就是 Gfinal。這裡 Gmid−term和 Gfinal分別為學生的期中和期末成績。
現在的問題是,每次考試都產生一張獨立的成績單。本題就請你編寫程式,把不同的成績單合為一張。
輸入格式:
輸入在第一行給出3個整數,分別是 P(做了線上程式設計作業的學生數)、M(參加了期中考試的學生數)、N(參加了期末考試的學生數)。每個數都不超過10000。
接下來有三塊輸入。第一塊包含 P 個線上程式設計成績 Gp;第二塊包含 M 個期中考試成績 Gmid−term;第三塊包含 N 個期末考試成績 Gfinal。每個成績佔一行,格式為:學生學號 分數。其中學生學號為不超過20個字元的英文字母和數字;分數是非負整數(程式設計總分最高為900分,期中和期末的最高分為100分)。
輸出格式:
打印出獲得合格證書的學生名單。每個學生佔一行,格式為:
學生學號 Gp Gmid−term Gfinal G
如果有的成績不存在(例如某人沒參加期中考試),則在相應的位置輸出“−1”。輸出順序為按照總評分數(四捨五入精確到整數)遞減。若有並列,則按學號遞增。題目保證學號沒有重複,且至少存在1個合格的學生。
輸入樣例:
6 6 7
01234 880
a1903 199
ydjh2 200
wehu8 300
dx86w 220
missing 400
ydhfu77 99
wehu8 55
ydjh2 98
dx86w 88
a1903 86
01234 39
ydhfu77 88
a1903 66
01234 58
wehu8 84
ydjh2 82
missing 99
dx86w 81
輸出樣例:
missing 400 -1 99 99
ydjh2 200 98 82 88
dx86w 220 88 81 84
wehu8 300 55 84 84
思路:
輸入時完成合並,然後篩選出合格的學生,最後對合格的學生進行排序。
程式碼:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
typedef struct{
char id[21];
int p,m,f,g;
}Stu_list;
int cmp(const void *a ,const void *b){
Stu_list *c=(Stu_list *)a;
Stu_list *d=(Stu_list *)b;
if(c->g!=d->g) return d->g - c->g;
else return d->id - c->id;
}
int main(){
int P,M,N;//P(做了線上程式設計作業的學生數)、M(參加了期中考試的學生數)、N(參加了期末考試的學生數)
scanf("%d %d %d",&P,&M,&N);
Stu_list l[10000];//成績單
int cnt=0;//cnt記錄成績單上有多少人
char temp_id[21];
int score;
for(int i=0;i<P;++i){
scanf("%s %d",temp_id,&score);
if(score>=200){
strcpy(l[cnt].id,temp_id),l[cnt].p=score;
l[cnt].m=-1,l[cnt++].f=-1;
}
}
for(int i=0;i<M;++i){
scanf("%s %d",temp_id,&score);
for(int j=0;j<cnt;++j){
if(strcmp(temp_id,l[j].id)==0){
l[j].m=score;
break;
}
}
}
for(int i=0;i<N;++i){
scanf("%s %d",temp_id,&score);
for(int j=0;j<cnt;++j){
if(strcmp(temp_id,l[j].id)==0){
l[j].f=score;
break;
}
}
}
for(int i=0;i<cnt;++i){
if((l[i].m>l[i].f)){
l[i].g=l[i].m*0.4+l[i].f*0.6;
if((l[i].m*0.4+l[i].f*0.6)-l[i].g>=0.5) ++l[i].g;
}
else l[i].g=l[i].f;
}
qsort(l,cnt,sizeof(l[0]),cmp);
for(int i=0;i<cnt;++i){
if(l[i].g>=60) printf("%s %d %d %d %d\n",l[i].id,l[i].p,l[i].m,l[i].f,l[i].g);
}
return 0;
}
出現的問題:
測試點3執行超時。
第一次修改:
思路基本和開始時一樣,原來合併成績單時對學號進行遍歷匹配,雖然每次都及時break,但超時還是不可避免的。於是將學號的匹配改為了折半查詢匹配,這裡用到了庫函式bsearch(),這裡是bsearch()的用法:
void *bsearch(const void *key, const void *base, size_t num, size_t size, int (*cmp)(const void *, const void *));
key 指向要查詢的元素
base 指向進行查詢的陣列
num 陣列中元素的個數
size 陣列中每個元素的大小,一般用sizeof()表示
cmp 比較兩個元素的函式,定義比較規則。
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
typedef struct{
char id[21];
int p,m,f,g;
}Stu_list;
int cmp_one(const void *a,const void *b)
{
Stu_list *p=(Stu_list *)a;
Stu_list *q=(Stu_list *)b;
return strcmp(p->id,q->id);
}
int cmp_two(const void *a ,const void *b){
Stu_list *c=(Stu_list *)a;
Stu_list *d=(Stu_list *)b;
if(c->g!=d->g) return d->g - c->g;
else return strcmp(c->id,d->id);
}
int cmp_bsearch(const void *str, const void *stu){
Stu_list *s=(Stu_list *)stu;
char *c=(char *)str;
return strcmp(c,s->id);
}
int main(){
int P,M,N;//P(做了線上程式設計作業的學生數)、M(參加了期中考試的學生數)、N(參加了期末考試的學生數)
scanf("%d %d %d",&P,&M,&N);
Stu_list *s;//指向學號匹配成功的學生
Stu_list l[P];//成績單
int cnt=0;//cnt記錄成績單上有多少人
char temp_id[21];
int score;
for(int i=0;i<P;++i){
scanf("%s %d",temp_id,&score);
strcpy(l[cnt].id,temp_id),l[cnt].p=score;
l[cnt].m=-1,l[cnt].f=-1,l[cnt++].g=0;
}
qsort(l,cnt,sizeof(l[0]),cmp_one);//沒做程式設計作業的學生不可能合格,因此在這裡進行一次按學號升序排序就可以了
for(int i=0;i<M;++i){
scanf("%s %d",temp_id,&score);
s=(Stu_list *)bsearch(temp_id,l,cnt,sizeof(l[0]),cmp_bsearch);//折半查詢
if(s!=NULL) s->m=score;//學號匹配成功,錄入期中成績
}
for(int i=0;i<N;++i){
scanf("%s %d",temp_id,&score);
s=(Stu_list *)bsearch(temp_id,l,cnt,sizeof(l[0]),cmp_bsearch);//折半查詢
if(s!=NULL){//學號匹配成功,錄入期末成績並計算出總成績
s->f=score;
if(s->f!=-1){
if(s->f>=s->m){
s->g=s->f;
}
else{
s->g=(int)(0.4*s->m+0.6*s->f+0.5);
}
}
}
}
qsort(l,cnt,sizeof(l[0]),cmp_two);//對成績單進行兩級排序
for(int i=0;i<cnt;++i){
if(l[i].p>=200&&l[i].g>=60) printf("%s %d %d %d %d\n",l[i].id,l[i].p,l[i].m,l[i].f,l[i].g);
}//輸出合格者成績單
return 0;
}