2018 團隊設計天梯賽題解---華山論劍組
寫在前面
2018 年度的團隊設計天梯賽前幾天結束了。但是成績真的是慘不忍睹。。。畢竟是團隊的比賽,如果團隊平均水平不高的話,單憑一個人,分再高也很難拉起來(當然,一個人能單挑一個隊的大神除外)。
說實話吧,其實這段話原本不想寫,一個是團隊成績並不好,另一個也是自己發揮的也不好,沒有達到目標分數。但是想想不管怎樣都是一種經歷,所以還是寫了這段話。
總結一下其實很多題放在平時做的話其實並不難,關鍵是心得放下來。但是一到比賽的氛圍就不一樣了,周圍全是別的學校的大牛,又加上有時間限制和周圍的一堆監考人員圍著走,想要放平心態其實是不容易的。這種東西還真得多經歷才會有感覺。好了,今天寫了一部分的題,來記錄下題解:
L1-1 天梯賽座位分配(20 分)
天梯賽每年有大量參賽隊員,要保證同一所學校的所有隊員都不能相鄰,分配座位就成為一件比較麻煩的事情。為此我們制定如下策略:假設某賽場有 N 所學校參賽,第 i 所學校有 M[i] 支隊伍,每隊 10 位參賽選手。令每校選手排成一列縱隊,第 i+1 隊的選手排在第 i 隊選手之後。從第 1 所學校開始,各校的第 1 位隊員順次入座,然後是各校的第 2 位隊員…… 以此類推。如果最後只剩下 1 所學校的隊伍還沒有分配座位,則需要安排他們的隊員隔位就坐。本題就要求你編寫程式,自動為各校生成隊員的座位號,從 1 開始編號。
輸入格式:
輸入在一行中給出參賽的高校數 N (不超過100的正整數);第二行給出 N 個不超過10的正整數,其中第 i 個數對應第 i 所高校的參賽隊伍數,數字間以空格分隔。
輸出格式:
從第 1 所高校的第 1 支隊伍開始,順次輸出隊員的座位號。每隊佔一行,座位號間以 1 個空格分隔,行首尾不得有多餘空格。另外,每所高校的第一行按“#X”輸出該校的編號X,從 1 開始。
輸入樣例:
3
3 4 2
輸出樣例:
#1
1 4 7 10 13 16 19 22 25 28
31 34 37 40 43 46 49 52 55 58
61 63 65 67 69 71 73 75 77 79
#2
2 5 8 11 14 17 20 23 26 29
32 35 38 41 44 47 50 53 56 59
62 64 66 68 70 72 74 76 78 80
82 84 86 88 90 92 94 96 98 100
#3
3 6 9 12 15 18 21 24 27 30
33 36 39 42 45 48 51 54 57 60
第一題其實意思不難理解,就是坑有點多,思路就是模擬題意去做就行了。普遍的坑就是第二個測試點和第三個測試點。
對於第二個測試點過不了的情況,可以試試下面的輸入資料:
2
5 1
如果結果對了,第二個測試點也就過了。
對於第三個測試點可以試試只有一個學校的情況:
1
2
最後是 AC 程式碼:
/**
* 先分配第一個學校第一個隊的第一個隊員,再分配第二個學校的第一個隊的第一個隊員,
* 再分配第三個學校的第一個隊的第一個隊員... 最後一個學校的第一個隊的最後一個隊員。
* 所有學校的第一個隊分配完畢,接下來是第一個學校的第二個隊的第一個隊員,
* 第二個學校的第二個隊的第一個隊員,第三個學校的第二個隊的第一個隊員...
*/
#include <iostream>
using namespace std;
const int MAXN = 110;
int schoolNum[MAXN]; // 記錄每個學校的隊數
int peo[MAXN][10][10]; // 記錄每個隊員的編號
int main() {
int n;
int maxx = 0;
cin >> n;
for (int i = 0; i < n; i++) {
scanf("%d", schoolNum + i);
maxx = max(maxx, schoolNum[i]);
}
int t, lastSchool = -1; // 上一次分配位置的隊員所在的學校
int startPos = 0; // 開始位置
for (int j = 0; j < maxx; j++) { // 第 j 個隊
for (int k = 0; k < 10; k++) { // 每個隊 10 個人
for (int i = 0; i < n; i++) { // n 個學校
if (schoolNum[i] <= j) {
continue;
}
// 如果上一次分配位置的隊員和現在分配位置的隊員在同一個學校
// 那麼證明是同一所學校的隊員,需要間隔一個位置。
if (lastSchool == i) {
startPos += 2;
} else { // 否則證明是不同的學校的隊員,相鄰座就好
startPos++;
}
peo[i][j][k] = startPos;
lastSchool = i; // 更新上一次分配位置的隊員所在的學校
}
}
}
for (int i = 0; i < n; i++) {
cout << "#" << (i+1) << endl;
for (int j = 0; j < schoolNum[i]; j++) {
for (int k = 0; k < 10; k++) {
if (k) {
cout << " ";
}
cout << peo[i][j][k];
}
cout << endl;
}
}
return 0;
}
L1-2 倒數第N個字串(15 分)
給定一個完全由小寫英文字母組成的字串等差遞增序列,該序列中的每個字串的長度固定為 L,從 L 個 a 開始,以 1 為步長遞增。例如當 L 為 3 時,序列為 { aaa, aab, aac, …, aaz, aba, abb, …, abz, …, zzz }。這個序列的倒數第27個字串就是 zyz。對於任意給定的 L,本題要求你給出對應序列倒數第 N 個字串。
輸入格式:
輸入在一行中給出兩個正整數 L(2 ≤ L ≤ 6)和 N(≤10
5
)。
輸出格式:
在一行中輸出對應序列倒數第 N 個字串。題目保證這個字串是存在的。
輸入樣例:
3 7417
輸出樣例:
pat
這個其實類似於給出一個整數 x
,再給出一個數 n
,然後求出 x
一直往前減 1 ,直到減了 n - 1
次的值(因為 x
本身是倒數第一個數,所以只需減 n - 1
次)。
對於字串也是一樣的。比如:zzz
的前一個字串就是 zzy
,zza
的前一個字串就是 zyz
。所以我們只需要對一個字串模擬整數的自減就可以了。下面是 AC 程式碼:
/**
* 用字串模擬整數的自減過程
*/
#include <iostream>
#include <string>
using namespace std;
int main() {
int n, m;
cin >> n >> m;
string str;
// 構造最末尾的字串
for (int i = 0; i < n; i++) {
str.append(1, 'z');
}
int size = str.length();
// 減 m-1 次,因為 str 是倒數第一個字串
for (int i = 0; i < m-1; i++) {
for (int j = size-1; j >= 0; --j) {
if (str[j] > 'a') {
str[j]--;
// 將當前字元後面的所有字元置為 'z',例:zza 的前一個字串為 zyz
for (int t = j+1; t < size; t++) {
str[t] = 'z';
}
break;
}
}
}
cout << str << endl;
return 0;
}
L1-3 打折(5 分)
去商場淘打折商品時,計算打折以後的價錢是件頗費腦子的事情。例如原價 ¥988,標明打 7 折,則折扣價應該是 ¥988 x 70% = ¥691.60。本題就請你寫個程式替客戶計算折扣價。
輸入格式:
輸入在一行中給出商品的原價(不超過1萬元的正整數)和折扣(為[1, 9]區間內的整數),其間以空格分隔。
輸出格式:
在一行中輸出商品的折扣價,保留小數點後 2 位。
輸入樣例:
988 7
輸出樣例:
691.60
這個題目沒啥講的,注意下把第二個整數轉換成浮點數就好了,因為 整數 / 整數 = 整數
#include <iostream>
using namespace std;
int main() {
int n;
double m;
cin >> n >> m;
printf("%.2lf", n*m/10);
return 0;
}
L1-4 2018我們要贏(5 分)
2018年天梯賽的註冊邀請碼是“2018wmyy”,意思就是“2018我們要贏”。本題就請你用漢語拼音輸出這句話。
輸入格式:
本題沒有輸入。
輸出格式:
在第一行中輸出:“2018”;第二行中輸出:“wo3 men2 yao4 ying2 !”。
輸入樣例:
本題沒有輸入。
輸出樣例:
2018
wo3 men2 yao4 ying2 !
程式碼:
#include <iostream>
using namespace std;
int main() {
cout << "2018\nwo3 men2 yao4 ying2 !" << endl;
}
L1-5 電子汪(10 分)
據說汪星人的智商能達到人類 4 歲兒童的水平,更有些聰明汪會做加法計算。比如你在地上放兩堆小球,分別有 1 只球和 2 只球,聰明汪就會用“汪!汪!汪!”表示 1 加 2 的結果是 3。
本題要求你為電子寵物汪做一個模擬程式,根據電子眼識別出的兩堆小球的個數,計算出和,並且用汪星人的叫聲給出答案。
輸入格式:
輸入在一行中給出兩個 [1, 9] 區間內的正整數 A 和 B,用空格分隔。
輸出格式:
在一行中輸出 A + B 個Wang!。
輸入樣例:
2 1
輸出樣例:
Wang!Wang!Wang!
這個也沒有什麼講的,程式碼:
#include <iostream>
using namespace std;
int main() {
int a, b;
cin >> a >> b;
a += b;
for (int i = 0; i < a; i++) {
cout << "Wang!";
}
return 0;
}
L1-6 福到了(15 分)
“福”字倒著貼,寓意“福到”。不論到底算不算民俗,本題且請你編寫程式,把各種漢字倒過來輸出。這裡要處理的每個漢字是由一個 N × N 的網格組成的,網格中的元素或者為字元 @ 或者為空格。而倒過來的漢字所用的字元由裁判指定。
輸入格式:
輸入在第一行中給出倒過來的漢字所用的字元、以及網格的規模 N (不超過100的正整數),其間以 1 個空格分隔;隨後 N 行,每行給出 N 個字元,或者為 @ 或者為空格。
輸出格式:
輸出倒置的網格,如樣例所示。但是,如果這個字正過來倒過去是一樣的,就先輸出bu yong dao le,然後再用輸入指定的字元將其輸出。
輸入樣例 1:
$ 9
@ @@@@@
@@@ @@@
@ @ @
@@@ @@@
@@@ @@@@@
@@@ @ @ @
@@@ @@@@@
@ @ @ @
@ @@@@@
輸出樣例 1:
$$$$$ $
$ $ $ $
$$$$$ $$$
$ $ $ $$$
$$$$$ $$$
$$$ $$$
$ $ $
$$$ $$$
$$$$$ $
輸入樣例 2:
& 3
@@@
@
@@@
輸出樣例 2:
bu yong dao le
&&&
&
&&&
通過測試樣例可以發現:其輸出的時候是將輸入的字串上下順序顛倒、左右順序顛倒輸出。
那麼如果輸入的 n
行字串中,第 i
行和第 n-i
行是一樣的,並且第 i
行是一個迴文字串(第 x
個字元和第 m-x
個字元相等。m
為字串長度)。那麼就可以輸出不用倒了。
AC程式碼:
#include <iostream>
#include <vector>
#include <string>
using namespace std;
// 判斷某個字串是否是迴文字串
bool isHuiwen(string &str) {
int len = str.length();
for (int i = 0; i < len/2; ++i) {
if (str[i] != str[len-1-i]) {
return false;
}
}
return true;
}
// 判斷某個樣例是否是不用倒了
bool judge(vector<string> &vec) {
int size = vec.size();
for (int i = 0; i < size/2; i++) {
// 如果第 i 行字串和倒數第 i 行字串不相等
// 或者第 i 行字串不是迴文字串,那麼返回 false
if (vec[i] != vec[size-1-i] || isHuiwen(vec[i]) == false) {
return false;
}
}
return true;
}
int main() {
char c;
int n;
vector<string> vec; // 儲存每行字串的動態陣列
vec.clear();
cin >> c >> n;
getchar(); // 耗掉輸入緩衝區中的換行符
for (int i = 0; i < n; i++) {
string str;
getline(cin, str);
vec.push_back(str);
}
// 判斷是否不用倒了
if (judge(vec)) {
cout << "bu yong dao le" << endl;
}
// 倒過來輸出
int size = vec.size();
int len;
for (int i = size - 1; i >= 0; --i) {
len = vec[i].length();
for (int j = len - 1; j >= 0; --j) {
if (vec[i][j] != ' ') {
cout << c;
} else {
cout << ' ';
}
}
cout << endl;
}
return 0;
}
L1-7 誰是贏家(10 分)
某電視臺的娛樂節目有個表演評審環節,每次安排兩位藝人表演,他們的勝負由觀眾投票和 3 名評委投票兩部分共同決定。規則為:如果一位藝人的觀眾票數高,且得到至少 1 名評委的認可,該藝人就勝出;或藝人的觀眾票數低,但得到全部評委的認可,也可以勝出。節目保證投票的觀眾人數為奇數,所以不存在平票的情況。本題就請你用程式判斷誰是贏家。
輸入格式:
輸入第一行給出 2 個不超過 1000 的正整數 Pa 和 Pb,分別是藝人 a 和藝人 b 得到的觀眾票數。題目保證這兩個數字不相等。隨後第二行給出 3 名評委的投票結果。數字 0 代表投票給 a,數字 1 代表投票給 b,其間以一個空格分隔。
輸出格式:
按以下格式輸出贏家:
The winner is x: P1 + P2
其中 x 是代表贏家的字母,P1 是贏家得到的觀眾票數,P2 是贏家得到的評委票數。
輸入樣例:
327 129
1 0 1
輸出樣例:
The winner is a: 327 + 1
根據題目意思做就好了:
#include <iostream>
using namespace std;
int main() {
int a, b;
int aa, bb, cc;
cin >> a >> b >> aa >> bb >> cc;
if (a > b && aa+bb+cc < 3) {
printf("The winner is %c: %d + %d\n", 'a', a, 3-aa-bb-cc);
} else if (b > a && aa+bb+cc > 0) {
printf("The winner is %c: %d + %d\n", 'b', b, aa+bb+cc);
} else if (a < b && aa+bb+cc == 0) {
printf("The winner is %c: %d + %d\n", 'a', a, 3);
} else if (b < a && aa+bb+cc == 3) {
printf("The winner is %c: %d + %d\n", 'b', b, 3);
}
return 0;
}
L1-8 猜數字(20 分)
一群人坐在一起,每人猜一個 100 以內的數,誰的數字最接近大家平均數的一半就贏。本題就要求你找出其中的贏家。
輸入格式:
輸入在第一行給出一個正整數N(≤10
4
)。隨後 N 行,每行給出一個玩家的名字(由不超過8個英文字母組成的字串)和其猜的正整數(≤ 100)。
輸出格式:
在一行中順序輸出:大家平均數的一半(只輸出整數部分)、贏家的名字,其間以空格分隔。題目保證贏家是唯一的。
輸入樣例:
7
Bob 35
Amy 28
James 98
Alice 11
Jack 45
Smith 33
Chris 62
輸出樣例:
22 Amy
對於這道題,用 STL 中的 map 結構去處理會比較方便,當然,C語言是沒有 STL 的,那麼我們可以用一個結構體,結構體裡面有一個 char
型陣列,用來儲存每個人的名字,然後是一個 int
型變數,用來儲存每個人猜的數字。
之後定義一個該結構體陣列,表示所有的人的資訊。最後對這個結構體陣列中的資訊進行讀取就好了。
這裡用的 C++ STL 中的 map 做的,程式碼:
#include <iostream>
#include <string>
#include <map>
#include <cmath>
using namespace std;
int main() {
int n;
cin >> n;
string name;
int num;
int sum = 0;
map<string, int> res; // 名字--分數 鍵值對
for (int i = 0; i < n; i++) {
cin >> name >> num;
res[name] = num;
sum += num;
}
sum /= (n*2);
int close_ = 100;
map<string, int>::iterator it = res.begin();
map<string, int>::iterator resIndex = res.begin();
for (; it != res.end(); ++it) {
if (close_ > abs(it->second - sum)) {
close_ = abs(it->second - sum);
resIndex = it;
}
}
cout << sum << " " << resIndex->first << endl;
return 0;
}
L2-1 分而治之(25 分)
分而治之,各個擊破是兵家常用的策略之一。在戰爭中,我們希望首先攻下敵方的部分城市,使其剩餘的城市變成孤立無援,然後再分頭各個擊破。為此參謀部提供了若干打擊方案。本題就請你編寫程式,判斷每個方案的可行性。
輸入格式:
輸入在第一行給出兩個正整數 N 和 M(均不超過10 000),分別為敵方城市個數(於是預設城市從 1 到 N 編號)和連線兩城市的通路條數。隨後 M 行,每行給出一條通路所連線的兩個城市的編號,其間以一個空格分隔。在城市資訊之後給出參謀部的系列方案,即一個正整數 K (≤ 100)和隨後的 K 行方案,每行按以下格式給出:
Np v[1] v[2] … v[Np]
其中 Np 是該方案中計劃攻下的城市數量,後面的系列 v[i] 是計劃攻下的城市編號。
輸出格式:
對每一套方案,如果可行就輸出YES,否則輸出NO。
輸入樣例:
10 11
8 7
6 8
4 5
8 4
8 1
1 2
1 4
9 8
9 1
1 10
2 4
5
4 10 3 8 4
6 6 1 7 5 4 9
3 1 8 4
2 2 8
7 9 8 7 6 5 4 2
輸出樣例:
NO
YES
YES
NO
NO
這題比賽的時候沒看清題意,真的是可惜。
其實題目就是給你一個圖的邊的資訊,之後每次給你若干個節點,當把這些節點和其他節點相連的邊都去除之後(即為題目中的攻破該城市),判斷這個圖是否還有任意一條邊相連(是否每個城市都是孤立的),如果有,那麼證明方案不可行,否則方案可行。
需要注意的是如果採用二維陣列儲存圖的資訊,最後兩個測試點是會記憶體超限的,另一種方法是用結構體儲存邊的資訊,這樣就不需要使用二維陣列了。
/**
* 用二維陣列儲存圖,最後兩個測試點記憶體超限!!
*/
/**
#include <iostream>
#include <vector>
#include <cstring>
using namespace std;
const int MAXN = 1e4+10;
int edge[MAXN][MAXN];
int main() {
int n, m;
cin >> n >> m;
int start, end;
for (int i = 0; i < m; i++) {
scanf("%d %d", &start, &end);
// 初始狀態如果兩個城市有邊相連標記為 1
edge[start][end] = edge[end][start] = 1;
}
int k, num, t;
cin >> k;
int nodeCount;
for (int i = 0; i < k; i++) {
scanf("%d", &num);
nodeCount = 0;
for (int j = 0; j < num; j++) {
scanf("%d", &t);
for (int l = 1; l <= n; l++) {
// 攻破該城市,將所有和該城市相連的邊置為 -1,
// 標記為已經攻破並孤立該城市
if (edge[t][l] == 1) {
edge[t][l] = edge[l][t] = -1;
}
}
}
bool canFinish = true;
for (int l = 1; l <= n; l++) {
for (int j = 1; j <= n; j++) {
// 如果發現不同城市之間有任何一條邊相連,
// 那麼證明這個方案是失敗的
if (edge[l][j] == 1) {
canFinish = false;
} else if (edge[l][j] == -1) { // 復原圖,以便下次使用
edge[l][j] = 1;
}
}
}
if (canFinish) {
cout << "YES" << endl;
} else {
cout << "NO" << endl;
}
}
return 0;
}
*/
/**
* 用結構體來儲存邊的資訊,AC
*/
#include <iostream>
#include <cstring>
using namespace std;
const int MAXN = 1e4+10;
struct Edge {
int x, y;
};
Edge edge[MAXN];
bool mark[MAXN]; // 標記節點是否被攻破
int main() {
int n, m;
cin >> n >> m;
int start, end;
for (int i = 0; i < m; i++) {
scanf("%d %d", &start, &end);
edge[i].x = start;
edge[i].y = end;
}
int k, num, t;
bool canFinish;
cin >> k;
for (int i = 0; i < k; i++) {
canFinish = true;
// 復原節點為未被攻破狀態
memset(mark, 0, sizeof(bool)*MAXN);
scanf("%d", &num);
for (int j = 0; j < num; j++) {
scanf("%d", &t);
mark[t] = true; // 節點 t 被攻破
}
// 遍歷每一條邊,如果某條邊的兩個頂點都沒被攻破,
// 證明這個方案是不行的
for (int i = 0; i < m; i++) {
if (mark[edge[i].x] == false && mark[edge[i].y] == false) {
canFinish = false;
break;
}
}
if (!canFinish) {
cout << "NO" << endl;
} else {
cout << "YES" << endl;
}
}
}
L2-2 小字輩(25 分)
L2-2 小字輩(25 分)
本題給定一個龐大家族的家譜,要請你給出最小一輩的名單。
輸入格式:
輸入在第一行給出家族人口總數 N(不超過 100 000 的正整數) —— 簡單起見,我們把家族成員從 1 到 N 編號。隨後第二行給出 N 個編號,其中第 i 個編號對應第 i 位成員的父/母。家譜中輩分最高的老祖宗對應的父/母編號為 -1。一行中的數字間以空格分隔。
輸出格式:
首先輸出最小的輩分(老祖宗的輩分為 1,以下逐級遞增)。然後在第二行按遞增順序輸出輩分最小的成員的編號。編號間以一個空格分隔,行首尾不得有多餘空格。
輸入樣例:
9
2 6 5 5 -1 5 6 4 7
輸出樣例:
4
1 9
這題其實就是通過給定的輸入資料建立一顆多叉樹,然後從祖先開始往下遍歷,找到最小的輩數和處於最小輩的人的編號。
具體看程式碼吧:
/**
* 用陣列來儲存建立的多叉樹
*/
#include <vector>
#include <iostream>
using namespace std;
const int MAXN = 1e5+10;
const int ROOT_INDEX = 1e5+1; // 祖先所在陣列下標
struct Peo {
vector<int> children;
};
Peo peo[MAXN]; // 儲存下標對應人的所有孩子編號
int minnChild; // 儲存最小的輩數
vector<int> res; // 儲存處於最小輩的人的編號
// 獲取最小輩數
void getMinnChild(int index, int currentMinn) {
int size = peo[index].children.size();
if (size == 0) {
// 如果這個人沒有孩子,證明其可能是處於最小輩
minnChild = max(currentMinn, minnChild);
return ;
}
for (int i = 0; i < size; i++) {
getMinnChild(peo[index].children[i], currentMinn+1);
}
}
// 獲取處於最小輩的人的編號
void solve(int index, int currentBei) {
int size = peo[index].children.size();
if (size == 0) {
// 最小輩一定沒有孩子,並且其所處的輩數等於最小輩數
if (currentBei == minnChild) {
res.push_back(index);
}
return ;
}
for (int i = 0; i < size; i++) {
solve(peo[index].children[i], currentBei+1);
}
}
int main() {
int n, t;
cin >> n;
for (int i = 1; i <= n; i++) {
scanf("%d", &t);
if (t == -1) {
t = ROOT_INDEX;
}
peo[t].children.push_back(i);
}
// 從祖先開始遍歷
getMinnChild(ROOT_INDEX, 0);
solve(ROOT_INDEX, 0);
// 輸出
cout << minnChild << endl;
int size = res.size();
for (int i = 0; i < size; i++) {
if (i) {
cout << " ";
}
cout << res[i];
}
return 0;
}
L2-3 名人堂與代金券(25 分)
對於在中國大學MOOC(http://www.icourse163.org/ )學習“資料結構”課程的學生,想要獲得一張合格證書,總評成績必須達到 60 分及以上,並且有另加福利:總評分在 [G, 100] 區間內者,可以得到 50 元 PAT 代金券;在 [60, G) 區間內者,可以得到 20 元PAT代金券。全國考點通用,一年有效。同時任課老師還會把總評成績前 K 名的學生列入課程“名人堂”。本題就請你編寫程式,幫助老師列出名人堂的學生,並統計一共發出了面值多少元的 PAT 代金券。
輸入格式:
輸入在第一行給出 3 個整數,分別是 N(不超過 10 000 的正整數,為學生總數)、G(在 (60,100) 區間內的整數,為題面中描述的代金券等級分界線)、K(不超過 100 且不超過 N 的正整數,為進入名人堂的最低名次)。接下來 N 行,每行給出一位學生的賬號(長度不超過15位、不帶空格的字串)和總評成績(區間 [0, 100] 內的整數),其間以空格分隔。題目保證沒有重複的賬號。
輸出格式:
首先在一行中輸出發出的 PAT 代金券的總面值。然後按總評成績非升序輸出進入名人堂的學生的名次、賬號和成績,其間以 1 個空格分隔。需要注意的是:成績相同的學生享有並列的排名,排名並列時,按賬號的字母序升序輸出。
輸入樣例:
10 80 5
[email protected].edu.cn 78
[email protected].com 87
1001@qq.com 65
uh-oh@163.com 96
test@126.com 39
[email protected].com 87
[email protected].edu 80
[email protected].edu 88
[email protected].edu 80
ken@163.com 70
輸出樣例:
360
1 uh-oh@163.com 96
2 [email protected].edu 88
3 [email protected].com 87
3 [email protected].com 87
5 [email protected].edu 80
5 [email protected].edu 80
這道題對於使用 STL 的選手來說就是福利啊。
可以用一個結構體來儲存每一個學生的資訊(郵箱、成績),之後定義一個該結構體的陣列,然後根據題目 的意思對這個結構體陣列進行自定義排序。最後就是輸出了
對於輸出需要注意的是並列的學生是會佔用一個排名名額的,比如上面測試樣例中並列第 3 的後面那個學生把排名為 4 的位置佔用了,於是就沒有第 4 名了。但是對於最後一名(第 k
名是可以有多個學生並列的)。
程式碼:
#include <string>
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
// 儲存學生資訊的結構體:郵箱、分數
struct Peo {
string email;
int grade;
};
const int MAXN = 1e4+10;
Peo peo[MAXN];
// 自定義陣列的排序規則:先按成績排序,若成績相同,按郵箱升序
bool com(Peo p1, Peo p2) {
if (p1.grade == p2.grade) {
return p1.email < p2.email;
}
return p1.grade > p2.grade;
}
int main() {
int n, g, k, money = 0;
cin >> n >> g >> k;
string email;
int grade;
for (int i = 0; i < n; i++) {
cin >> email >> grade;
peo[i].email = email;
peo[i].grade = grade;
// 統計出發出了多少代金券
if (grade >= 60 && grade < g) {
money += 20;
} else if (grade >= g) {
money += 50;
}
}
sort(peo, peo + n, com);
cout << money << endl;
int lastGrade = -1; // 儲存上一個學生的成績
int currentLevel = 0; // 儲存當前學生的排名
for (int i = 0; i < k; i++) {
// 如果當前學生的成績不等於上一個學生的成績,
// 證明當前學生的排名在上一個學生的後一名
if (lastGrade != peo[i].grade) {
currentLevel = i + 1;
}
cout << currentLevel << " " <<
peo[i].email << " " << peo[i].grade << endl;
lastGrade = peo[i].grade;
}
// 輸出最後所有並列第 k 名的學生資訊
int i = k;
while (peo[i].grade == lastGrade) {
cout << currentLevel << " " <<
peo[i].email << " " << peo[i].grade << endl;
i++;
}
return 0;
}
好了,今天就只 AC 了這些題,後面的題沒有全部 AC,還是等做出來了再補充吧。