傳送門
https://pintia.cn/problem-sets/994805260223102976/problems/994805307551629312
題目
宋代史學家司馬光在《資治通鑒》中有一段著名的“德才論”:“是故才德全盡謂之圣人,才德兼亡謂之愚人,德勝才謂之君子,才勝德謂之小人。凡取人之術,茍不得圣人,君子而與之,與其得小人,不若得愚人?!?br> 現給出一批考生的德才分數,請根據司馬光的理論給出錄取排名。
輸入格式:
輸入第1行給出3個正整數,分別為:N(<=105),即考生總數;L(>=60),為錄取最低分數線,即德分和才分均不低于L的考生才有資格被考慮錄取;H(<100),為優先錄取線——德分和才分均不低于此線的被定義為“才德全盡”,此類考生按德才總分從高到低排序;才分不到但德分到線的一類考生屬于“德勝才”,也按總分排序,但排在第一類考生之后;德才分均低于H,但是德分不低于才分的考生屬于“才德兼亡”但尚有“德勝才”者,按總分排序,但排在第二類考生之后;其他達到最低線L的考生也按總分排序,但排在第三類考生之后。
隨后N行,每行給出一位考生的信息,包括:準考證號、德分、才分,其中準考證號為8位整數,德才分為區間[0, 100]內的整數。數字間以空格分隔。
輸出格式:
輸出第1行首先給出達到最低分數線的考生人數M,隨后M行,每行按照輸入格式輸出一位考生的信息,考生按輸入中說明的規則從高到低排序。當某類考生中有多人總分相同時,按其德分降序排列;若德分也并列,則按準考證號的升序輸出。
輸入樣例:
14 60 80
10000001 64 90
10000002 90 60
10000011 85 80
10000003 85 80
10000004 80 85
10000005 82 77
10000006 83 76
10000007 90 78
10000008 75 79
10000009 59 90
10000010 88 45
10000012 80 100
10000013 90 99
10000014 66 60
輸出樣例:
12
10000013 90 99
10000012 80 100
10000003 85 80
10000011 85 80
10000004 80 85
10000007 90 78
10000006 83 76
10000005 82 77
10000002 90 60
10000014 66 60
10000008 75 79
10000001 64 90
分析
這道題看著不是很難,但是時間要求控制在200ms以內,所以要考慮到排序方法的快慢,之前我用到了冒泡排序,結果有3個測試點超時,然后參考了別人的代碼,發現別人用的是C語言的快速排序qsort(),這種方法有一個優點就是對二維數組,整行進行交換順序,非常方便。最后我選用的也是這種方法解決的問題。
這道題中我踩到的坑很多:
1.開始用vector,因為是動態數組,比較方便,排序方法選用了STL中的sort()方法,這種方法是內省排序,但是這種方法不支持多維數組整組交換順序,所以最終放棄了;
2.之后我選用vector+qsort(),這種方法在使用時也出現了問題,我在使用的時候發現它只對前面幾組數據進行排序,而沒對所有數據進行排序,再查詢了資料后,才發現原來qsort()的排序必須對連續地址的數據進行排序,而vector是動態數組,會動態申請內存,所以數據不是連續放置的,因此vector+qsort()也失敗了;
3.接著我選用了靜態數組+qsort(),可是這里又遇到了問題,因為VS不支持靜態數組用未知變量聲明大小,所以要用到指針來聲明,而二維數組的做法更加麻煩,所以我最后放棄了VS,選用了Xcode,因為其支持C99標準,即可以用未知變量聲明數組大小,最終才解決這道題。
2017年3月17日更新
<h3>原先的第二點有誤,vector中的元素在內存中是連續存儲的。</h3>我出現的問題是由于qsort排序的第二個參數沒有取實際當前數組的參數,用的還是原來的大小,所以導致只對前面幾個元素進行排序。
vector可以動態擴容,動態擴容后,會重新進行內存拷貝,使得新生成的vector的內存仍舊是連續的。
<h3>由此對大家造成的誤解,本人深表歉意!</h3>
以下附上一段測試代碼。
#include <iostream>
#include <vector>
using namespace std;
int main(){
vector<int> v(5, 1);
for (vector<int>::iterator i = v.begin(); i != v.end(); ++i){
cout << &(*i) << " " << *i << endl;
}
cout << "=========================" << endl;
int cycle = 5;
while (cycle--){
v.push_back(cycle);
}
for (vector<int>::iterator i = v.begin(); i != v.end(); ++i){
cout << &(*i) << " " << *i << endl;
}
cout << "=========================" << endl;
cycle = 10;
while (cycle--){
v.push_back(cycle);
}
for (vector<int>::iterator i = v.begin(); i != v.end(); ++i){
cout << &(*i) << " " << *i << endl;
}
}
以下是輸出:
005ED628 1
005ED62C 1
005ED630 1
005ED634 1
005ED638 1
=========================
005EE7D8 1
005EE7DC 1
005EE7E0 1
005EE7E4 1
005EE7E8 1
005EE7EC 4
005EE7F0 3
005EE7F4 2
005EE7F8 1
005EE7FC 0
=========================
005F01E0 1
005F01E4 1
005F01E8 1
005F01EC 1
005F01F0 1
005F01F4 4
005F01F8 3
005F01FC 2
005F0200 1
005F0204 0
005F0208 9
005F020C 8
005F0210 7
005F0214 6
005F0218 5
005F021C 4
005F0220 3
005F0224 2
005F0228 1
005F022C 0
源代碼
//C/C++實現
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <vector>
using namespace std;
int compare(const void *a_t, const void *b_t){
int *a = (int *)a_t, *b = (int *)b_t;
if(a[3] != b[3]){
return a[3] - b[3]; //type升序
}
else if(a[1] + a[2] != b[1] + b[2]){
return (b[1] + b[2]) - (a[1] + a[2]); //總分降序
}
else if(a[1] != b[1]){
return b[1] - a[1]; //德分降序
}
else{
return a[0] - b[0]; //學號升序
}
}
int main() {
int n, l, h;
scanf("%d %d %d", &n, &l, &h);
int sno, de, cai, type, count = 0;
int sTable[n][4]; //n行4列,學號、德分、才分、類型
for(int i = 0; i < n; i++){
scanf("%d %d %d", &sno, &de, &cai);
//judge type
if(de >= l && cai >= l){
if(de >= h && cai >= h){
type = 1;
}
else if(de >= h && cai < h){
type = 2;
}
else if(de < h && cai < h && de >= cai){
type = 3;
}
else{
type = 4;
}
}
else{
type = 0;
count++;
}
sTable[i][0] = sno; sTable[i][1] = de; sTable[i][2] = cai; sTable[i][3] = type;
}
//quick sort
qsort(&sTable[0], n, sizeof(sTable[0]), compare);
printf("%d\n", n - count);
for(int i = count; i < n; i++){
printf("%d %d %d\n", sTable[i][0], sTable[i][1], sTable[i][2]);
}
return 0;
}