1. 程式人生 > >2018 團隊設計天梯賽題解---華山論劍組

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 的前一個字串就是 zzyzza 的前一個字串就是 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,還是等做出來了再補充吧。