2024 年 GPLT 團體程式設計天梯賽(個人感受 + 題解)

MarisaMagic發表於2024-04-25

前言

去年第一次參加天梯賽,拿了 \(158\) 分,沒有個人獎,團隊也差點打鐵(最後應該是遞補省三)。

今年天梯賽拿了 \(224\) 分,幸運地拿了個人國二。這次擔任的隊長,團隊也拿到了國二,感謝隊友們的努力付出。

今年好像很多人斷網,不過沒有發生在我身上哈哈哈(也許是因為我連的手機熱點)。對於我個人來說,比賽過程還是比較順暢的,題目難度個人認為比去年簡單(不過部分題目有點...抽象?),最後結果還算滿意。倒數第二題據說暴力都可以拿 \(21\) 分,我當時做完 \(\text{L3-1}\) 的時候還剩大概二十分鐘,然後就...笑了差不多二十分鐘,也許真應該繼續做下倒數第二題的(雖然好像打了暴力也不會國一🤡)。

總得來說結果還算不錯,比去年進步很多,隊友們還是很給力的。我的學校演算法競賽一直以來挺弱的,這次天梯賽算是一個好的開頭,我相信今後學校會越來越強的!

今年也許是我最後一次參加天梯賽了,當然到了大四想繼續玩也不是不行(如果大佬學弟們不嫌棄我的話😋)。



L1-1 程式設計解決一切

題目描述

程式設計解決一切 —— 本題非常簡單,就請你直接在螢幕上輸出這句話:\(\text{“Problem? The Solution: Programming.”}\)

輸入格式

輸出格式

在一行中輸出 Problem? The Solution: Programming.


解題思路

簽到題。

#include<bits/stdc++.h>
using namespace std;

#define ios ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)

int main(){
    ios;

    puts("Problem? The Solution: Programming.");

    return 0;
}


L1-2 再進去幾個人

題目描述

題幹太長了不想看,隨便輸出 \(B - A\) 就行。

輸入格式

輸入在一行中給出 2 個不超過 100 的正整數 \(A\)\(B\),其中 \(A\) 是進去的人數,\(B\) 是出來的人數。題目保證 \(B\)\(A\) 要大。

輸出格式

在一行中輸出使得房子變空的、需要再進去的人數。

測試資料

輸入樣例

4 7

輸出樣例

3

解題思路

輸出 \(B - A\) 即可。

#include<bits/stdc++.h>
using namespace std;

#define ios ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)

int main(){
    ios;

    int a, b; cin >> a >> b;
    cout << b - a << "\n";

    return 0;
}


L1-3 幫助色盲

題目描述

又是題幹老長的一道題,賽場上讀題的時候差點被繞暈了。大概意思就是說:

  • 前方有行人

    第一行輸出 - 即可。

    • 此時如果是綠燈,第二行輸出 move

    • 此時如果不是綠燈,第二行輸出 stop

  • 前方無行人

    • 如果是綠燈,第一行輸出 dudu,第二行輸出 move

    • 如果是紅燈,第一行輸出 biii,第二行輸出 stop

    • 如果是黃燈,第一行輸出 -,第二行輸出 stop

輸入格式

輸入在一行中給出兩個數字 \(A\)\(B\),其間以空格分隔。其中 \(A\) 是當前交通燈的顏色,取值為 \(0\) 表示紅燈、\(1\) 表示綠燈、\(2\) 表示黃燈;\(B\) 是前方行人的狀態,取值為 \(0\) 表示前方兩米內沒有同向行走的人、\(1\) 表示有。

輸出格式

根據輸入的狀態在第一行中輸出提示音:dudu 表示前方為綠燈,可以繼續前進;biii 表示前方為紅燈,應該止步;- 表示不做提示。在第二行輸出患者應該執行的動作:move 表示繼續前進、stop 表示止步。

測試資料

輸入樣例 1

0 0

輸出樣例 1

biii
stop

輸入樣例 2

1 1

輸出樣例 2

-
move

解題思路

按照題意判斷輸出結果即可。

#include<bits/stdc++.h>
using namespace std;

#define ios ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)

int main(){
    ios;

    int a, b; cin >> a >> b;
    if(b == 1){
        cout << "-" << "\n";
        cout << (a == 1 ? "move" : "stop") << "\n";
    }else{
        if(a == 0) cout << "biii\nstop" << "\n";
        else if(a == 1) cout << "dudu\nmove" << "\n";
        else cout << "-\nstop" << "\n";
    }

    return 0;
}


L1-4 四項全能

題目描述

給定全班總人數為 \(n\),有 \(m\) 項技能,每個技能分別有 \(k_1, k_2, \ldots , k_m\) 個人會,求至少有多少人會所有的 \(m\) 項技能。

輸入格式

輸入在第一行中給出 2 個正整數:\(n\)\(4 \le n \le 1000\))和 \(m\)\(1 < m \le \frac{n}{2}\)),分別對應全班人數和技能總數。隨後一行給出 \(m\) 個不超過 \(n\) 的正整數,其中第 \(i\) 個整數對應會第 \(i\) 項技能的人數。

輸出格式

輸出至少有多少人 \(m\) 項都會。

測試資料

輸入樣例

50 4
30 35 42 46

輸出樣例

3

解題思路

小學數學題(但是我不會),題幹中給出了一般情況的求解方法。 但有一些情況需要特判,例如當每個技能會的人的總數恰好等於 \(n \times m\) 時,此時所有人都會所有技能,故需要輸出 \(n\);又例如當人數 \(n\) 大於每個技能會的人的總數時,此時無人會所有技能,故需要輸出 \(0\)

這題我賽場上沒拿滿分哈哈哈😋

#include<bits/stdc++.h>
using namespace std;

#define ios ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)

int main(){
    ios;

    int n, m, sum = 0; cin >> n >> m;
    for(int i = 1, x; i <= m; i ++ ) cin >> x, sum += x;
    if(sum == n * m) cout << n << "\n";  // 所有人都會m個
    else if(n > sum) cout << 0 << "\n";  // 人個數大於總技能數,此時無人全會
    else cout << sum - (sum / n) * n << "\n"; // 題幹中的情況

    return 0;
}


L1-5 別再來這麼多貓娘了!

題目描述

你會得到一段由大小寫字母、數字、空格及 ASCII 碼範圍內的標點符號的文字,以及若干個違禁詞以及警告閾值,你需要首先檢查內容裡有多少違禁詞,如果少於閾值個,則簡單地將違禁詞替換為 <censored>;如果大於等於閾值個,則直接輸出一段警告並輸出有幾個違禁詞。

輸入格式

輸入第一行是一個正整數 \(N\) (\(1\le N \le 100\)),表示違禁詞的數量。接下來的 \(N\) 行,每行一個長度不超過 10 的、只包含大小寫字母、數字及 ASCII 碼範圍內的標點符號的單詞,表示應當遮蔽的違禁詞。
然後的一行是一個非負整數 \(k\) (\(0\le k\le 100\)),表示違禁詞的閾值。
最後是一行不超過 5000 個字元的字串,表示需要檢查的文字。
從左到右處理文字,違禁詞則按照輸入順序依次處理;對於有重疊的情況,無論計數還是替換,查詢完成後從違禁詞末尾繼續處理。

輸出格式

如果違禁詞數量小於閾值,則輸出替換後的文字;否則先輸出一行一個數字,表示違禁詞的數量,然後輸出 He Xie Ni Quan Jia!

測試資料

輸入樣例1

5
MaoNiang
SeQing
BaoLi
WeiGui
BuHeShi
4
BianCheng MaoNiang ba! WeiGui De Hua Ye Keyi Shuo! BuYao BaoLi NeiRong.

輸出樣例1

BianCheng <censored> ba! <censored> De Hua Ye Keyi Shuo! BuYao <censored> NeiRong.

輸入樣例2

5
MaoNiang
SeQing
BaoLi
WeiGui
BuHeShi
3
BianCheng MaoNiang ba! WeiGui De Hua Ye Keyi Shuo! BuYao BaoLi NeiRong.

輸出樣例2

3
He Xie Ni Quan Jia!

輸入樣例3

2
AA
BB
3
AAABBB

輸出樣例3

<censored>A<censored>B

輸入樣例4

2
AB
BB
3
AAABBB

輸出樣例4

AA<censored><censored>

輸入樣例5

2
BB
AB
3
AAABBB

輸出樣例5

AAA<censored>B

解題思路

這個題...真的一言難盡,賽場上超時了兩個點,一度以為是方法問題,而實際上是因為違禁詞中可能存在 <censored> 的子串,從而在替換過程中導致無限替換下去。因此只需要先將違禁詞替換成一個奇怪的東西,最後再全部替換回 <censored> 即可。

#include<bits/stdc++.h>
using namespace std;

#define ios ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)

vector<string> ban;
int n, k;
string s, tmp = "*****";

int main(){
    ios;

    cin >> n;
    ban.resize(n);
    for(int i = 0; i < n; i ++ ) cin >> ban[i];

    cin >> k;
    cin.ignore();
    getline(cin, s);

    int cnt = 0;
    for(auto &t : ban){
        int sz = t.size(), pos = s.find(t);
        while(pos != -1){
            cnt ++ ;
            s = s.substr(0, pos) + tmp + s.substr(pos + sz);
            pos = s.find(t);
        }
    }

    if(cnt >= k) cout << cnt << '\n' << "He Xie Ni Quan Jia!" << "\n";
    else{
        int sz = tmp.size(), pos = s.find(tmp);
        while(pos != -1){
            s = s.substr(0, pos) + "<censored>" + s.substr(pos + sz);
            pos = s.find(tmp);
        }
        cout << s << "\n";
    }

    return 0;
}


L1-6 蘭州牛肉麵

題目描述

請你寫程式幫助一家牛肉麵館的老闆統計一下,他們一天賣出各種品種的牛肉麵有多少碗,營業額一共有多少。

輸入格式

輸入第一行給出一個正整數 \(N\)\(\le 100\)),為牛肉麵的種類數量。這裡為了簡單起見,我們把不同種類的牛肉麵從 \(1\)\(N\) 編號,以後就用編號代替牛肉麵品種的名稱。第二行給出 \(N\) 個價格,第 \(i\) 個價格對應第 \(i\) 種牛肉面一碗的單價。這裡的價格是 \([0.01, 200.00]\) 區間內的實數,以元為單位,精確到分。
隨後是一天內客人買面的記錄,每條記錄佔一行,格式為:

品種編號 碗數

其中 碗數 保證是正整數。當對應的 品種編號 為 \(0\) 時,表示輸入結束。這個記錄不算在內。

輸出格式

首先輸出 \(N\) 行,第 \(i\) 行輸出第 \(i\) 種牛肉面賣出了多少碗。最後一行輸出當天的總營業額,仍然是以元為單位,精確到分。題目保證總營業額不超過 \(10^6\)

測試資料

輸入樣例

5
4.00 8.50 3.20 12.00 14.10
3 5
5 2
1 1
2 3
2 2
1 9
0 0

輸出樣例

10
5
5
0
2
126.70

解題思路

統計每個種類的個數,同時統計總營業額,按照要求輸出結果即可。

#include<bits/stdc++.h>
using namespace std;

#define ios ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)

int main(){
    // ios;

    int n; cin >> n;
    vector<double> a(n + 1);
    vector<int> cnt(n + 1, 0);
    for(int i = 1; i <= n; i ++ ) cin >> a[i];

    double sum = 0;
    int x, y;
    while(cin >> x >> y, x){
        cnt[x] += y;
        sum += a[x] * y;
    }
    for(int i = 1; i <= n; i ++ ) cout << cnt[i] << "\n";
    printf("%.2lf\n", sum);

    return 0;
}


L1-7 整數的持續性

題目描述

從任一給定的正整數 \(n\) 出發,將其每一位數字相乘,記得到的乘積為 \(n_1\)。以此類推,令 \(n_{i+1}\)\(n_i\) 的各位數字的乘積,直到最後得到一個個位數 \(n_m\),則 \(m\) 就稱為 \(n\) 的持續性。
本題就請你編寫程式,找出任一給定區間內持續性最長的整數。

輸入格式

輸入在一行中給出兩個正整數 \(a\)\(b\)\(1\le a \le b\le 10^9\)\((b−a) < 10^3\)),為給定區間的兩個端點。

輸出格式

首先在第一行輸出區間 \([a,b]\) 內整數最長的持續性。隨後在第二行中輸出持續性最長的整數。如果這樣的整數不唯一,則按照遞增序輸出,數字間以 \(1\) 個空格分隔,行首尾不得有多餘空格。

測試資料

輸入樣例

500 700

輸出樣例

5
679 688 697

解題思路

按照題意模擬,統計結果即可。

#include<bits/stdc++.h>
using namespace std;

#define ios ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)

int work(int x){
    int res = 0;
    while(x > 10){
        int tmp = 1;
        while(x) tmp *= x % 10, x /= 10; 
        x = tmp;
        res ++ ;
    }
    return res;
}

int main(){
    ios;

    int l, r; cin >> l >> r;
    vector<int> ans;
    int mx = 0;
    for(int i = l; i <= r; i ++ ){
        int cur = work(i);
        if(cur > mx) mx = cur, ans.clear(), ans.emplace_back(i);
        else if(cur == mx) ans.emplace_back(i);
    }

    cout << mx << "\n";
    for(int i = 0; i < (int)ans.size(); i ++ ) cout << ans[i] << " \n"[i == (int)ans.size() - 1];

    return 0;
}


L1-8 九宮格

題目描述

九宮格是一款數字遊戲,傳說起源於河圖洛書,現代數學中稱之為三階幻方。遊戲規則是:將一個 \(9\times 9\) 的正方形區域劃分為 \(9\)\(3\times 3\) 的正方形宮位,要求 \(1\)\(9\) 這九個數字中的每個數字在每一行、每一列、每個宮位中都只能出現一次。
本題並不要求你寫程式解決這個問題,只是對每個填好數字的九宮格,判斷其是否滿足遊戲規則的要求。

輸入格式

輸入首先在第一行給出一個正整數 \(n\)\(\le 10\)),隨後給出 \(n\) 個填好數字的九宮格。每個九宮格分 \(9\) 行給出,每行給出 \(9\) 個數字,其間以空格分隔。

輸出格式

對每個給定的九宮格,判斷其中的數字是否滿足遊戲規則的要求。滿足則在一行中輸出 \(1\),否則輸出 \(0\)

測試資料

輸入樣例

3
5 1 9 2 8 3 4 6 7
7 2 8 9 6 4 3 5 1
3 4 6 5 7 1 9 2 8
8 9 2 1 4 5 7 3 6
4 7 3 6 2 8 1 9 5
6 5 1 7 3 9 2 8 4
9 3 4 8 1 6 5 7 2
1 6 7 3 5 2 8 4 9
2 8 5 4 9 7 6 1 3
8 2 5 4 9 7 1 3 6
7 9 6 5 1 3 8 2 4
3 4 1 6 8 2 7 9 5
6 8 4 2 7 1 3 5 9
9 1 2 8 3 5 6 4 7
5 3 7 9 6 4 2 1 8
2 7 9 1 5 8 4 6 3
4 5 8 3 2 6 9 7 1
1 6 3 7 4 9 5 8 3
81 2 5 4 9 7 1 3 6
7 9 6 5 1 3 8 2 4
3 4 1 6 8 2 7 9 5
6 8 4 2 7 1 3 5 9
9 1 2 8 3 5 6 4 7
5 3 7 9 6 4 2 1 8
2 7 9 1 5 8 4 6 3
4 5 8 3 2 6 9 7 1
1 6 3 7 4 9 5 8 2

輸出樣例

1
0
0

解題思路

\(1\)\(9\) 這九個數字中的每個數字在 每一行、每一列、每個宮位 中都只能出現一次。 關鍵是恰好一次,不能出現其他數字(樣例也給出了這個特例)。寫幾個東西模擬判斷一下就行,很簡單(比較菜,寫得稍微有點長🥵)。

#include<bits/stdc++.h>
using namespace std;

#define ios ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
const int N = 10;

int g[N][N], n = 9;

bool check1(){ // 每一行
    for(int i = 1; i <= n; i ++ ){
        vector<bool> vis(10, false);
        for(int j = 1; j <= n; j ++ ){
            if(g[i][j] < 1 || g[i][j] > 9) return false;
            if(vis[g[i][j]]) return false;
            vis[g[i][j]] = true;
        }
        for(int i = 1; i <= 9; i ++ ) if(!vis[i]) return false;
    }
    return true;
}

bool check2(){ // 每一列
    for(int i = 1; i <= n; i ++ ){
        vector<bool> vis(10, false);
        for(int j = 1; j <= n; j ++ ){
            if(g[j][i] < 1 || g[j][i] > 9) return false;
            if(vis[g[j][i]]) return false;
            vis[g[j][i]] = true;
        }
        for(int i = 1; i <= 9; i ++ ) if(!vis[i]) return false;
    }
    return true;
}

bool check3(){ // 每一個九宮格
    for(int sx = 1; sx <= n; sx += 3)
        for(int sy = 1; sy <= n; sy += 3){
            vector<bool> vis(10, false);
            for(int i = sx; i <= sx + 2; i ++ )
                for(int j = sy; j <= sy + 2; j ++ ){
                    if(g[i][j] < 1 || g[i][j] > 9) return false;
                    if(vis[g[i][j]]) return false;
                    vis[g[i][j]] = true;
                }
            for(int i = 1; i <= 9; i ++ ) if(!vis[i]) return false;
        }
    return true;
}

int main(){
    ios;

    int T; cin >> T;
    while(T -- ){
        for(int i = 1; i <= n; i ++ )
            for(int j = 1; j <= n; j ++ )
                cin >> g[i][j];

        cout << (check1() && check2() && check3() ? "1" : "0") << "\n";
    }   

    return 0;
}


L2-1 魚與熊掌

題目描述

給定 \(n\) 個人對 \(m\) 種物品的擁有關係。對其中任意一對物品種類,請你統計有多少人能夠兼得?

輸入格式

輸入首先在第一行給出 2 個正整數,分別是:\(n\)\(\le 10^5\))為總人數(所有人從 \(1\)\(n\) 編號)、\(m\)\(2\le m\le 10^5\))為物品種類的總數(所有物品種類從 \(1\)\(m\) 編號)。隨後 \(n\) 行,第 \(i\) 行(\(1\le i\le n\))給出編號為 \(i\) 的人所擁有的物品種類清單,格式為:

K M[1] M[2] ... M[K]

其中 \(K\)\(\le 10^3\))是該人擁有的物品種類數量,後面的 \(M[*]\) 是物品種類的編號。題目保證每個人的物品種類清單中都沒有重複給出的種類。
最後是查詢資訊:首先在一行中給出查詢總量 \(Q\)\(\le 100\)),隨後 \(Q\) 行,每行給出一對物品種類編號,其間以空格分隔。題目保證物品種類編號都是合法存在的。

輸出格式

對每一次查詢,在一行中輸出兩種物品兼得的人數。

測試資料

輸入樣例

4 8
3 4 1 8
4 7 1 8 4
5 6 5 1 2 3
4 3 2 4 8
3
2 3
7 6
8 4

輸出樣例

2
0
3

解題思路

大水題,資料很弱。直接用 set 儲存每個物品對應的人,對於每個詢問直接暴力判斷即可。

#include<bits/stdc++.h>
using namespace std;

#define ios ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
const int N = 1e5 + 10;

set<int> st[N];
int n, m, q;

int main(){
    ios;

    cin >> n >> m;
    for(int i = 1, k; i <= n; i ++ ){
        cin >> k;
        for(int j = 1, x; j <= k; j ++ ) cin >> x, st[x].insert(i);
    }

    cin >> q;
    for(int i = 1, a, b; i <= q; i ++ ){
        cin >> a >> b;
        int cnt = 0;
        for(auto &x : st[a]) cnt += (st[b].count(x));
        cout << cnt << "\n";
    }

    return 0;
}


L2-2 懂蛇語

題目描述

首先提取 A 的每個字的首字母,然後把整句話替換為另一句話 B,B 中每個字的首字母與 A 中提取出的字母依次相同。例如二當家說“九點下班哈”,對應首字母縮寫是 JDXBH,他們解釋為實際想說的是“京東新百貨”……

本題就請你寫一個蛇語的自動翻譯工具,將輸入的蛇語轉換為實際要表達的句子。

輸入格式

輸入第一行給出一個正整數 \(N\)\(\le 10^5\)),為蛇語詞典中句子的個數。隨後 \(N\) 行,每行用漢語拼音給出一句話。每句話由小寫英文字母和空格組成,每個字的拼音由不超過 6 個小寫英文字母組成,兩個字的拼音之間用空格分隔。題目保證每句話總長度不超過 50 個字元,用回車結尾。注意:回車不算句中字元。

隨後在一行中給出一個正整數 \(M\)\(\le 10^3\)),為查詢次數。後面跟 \(M\) 行,每行用漢語拼音給出需要查詢的一句話,格式同上。

輸出格式

對每一句查詢,在一行中輸出其對應的句子。如果句子不唯一,則按整句的字母序輸出,句子間用 | 分隔。如果查不到,則將輸入的句子原樣輸出。

注意:輸出句子時,必須保持句中所有字元不變,包括空格。

測試資料

輸入樣例

8
yong yuan de shen
yong yuan de she
jing dong xin bai huo
she yu wo ye hui shuo yi dian dian
liang wei bu yao chong dong
yi  dian dian
ni hui shuo she yu a
yong yuan de sha
7
jiu dian xia ban ha
shao ye wu ya he shui you dian duo
liu wan bu yao ci dao
ni hai shi su yan a
yao diao deng
sha ye ting bu jian
y y d s

輸出樣例

jing dong xin bai huo
she yu wo ye hui shuo yi dian dian
liang wei bu yao chong dong
ni hui shuo she yu a
yi  dian dian
sha ye ting bu jian
yong yuan de sha|yong yuan de she|yong yuan de shen


解題思路

map<string, vector<string> > 儲存每個首字母構成的串對應的所有句子,對於每個詢問,將詢問的句子處理成首字母構成的串,按照要求輸出結果即可。

對於首字母的判斷,我給字串前面加了一個空格,這樣只要是前面一個位置是空格的字元,就是首字母。

如果有多個對應,需要按照字典序輸出結果,所以排個序就好了。

#include<bits/stdc++.h>
using namespace std;

#define ios ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)

int n, q;
map<string, vector<string> > mp;
string s;

inline string work(string s){
    string res = "";
    int sz = s.size();
    s = " " + s; // 方便首字母判斷
    for(int i = 1; i <= sz; i ++ )
        if(isalpha(s[i]) && s[i - 1] == ' ') res += s[i]; // 前一個時空格的字元為首字母
    return res;
}

int main(){
    ios;

    cin >> n;
    cin.ignore();
    for(int i = 1; i <= n; i ++ ){
        getline(cin, s);
        mp[work(s)].emplace_back(s);
    }
    for(auto &[_, v] : mp) sort(begin(v), end(v)); // 要排序

    cin >> q;
    cin.ignore();
    for(int i = 1; i <= q; i ++ ){
        getline(cin, s);
        string t = work(s);
        if(!mp.count(t)) cout << s << "\n";
        else{
            int sz = mp[t].size();
            for(int i = 0; i < sz; i ++ ){
                if(i > 0) cout << "|";
                cout << mp[t][i];
            }
            cout << "\n";
        } 
    }

    return 0;
}


L2-3 滿樹的遍歷

題目描述

一棵“\(k\) 階滿樹”是指樹中所有非葉結點的度都是 \(k\) 的樹。給定一棵樹,你需要判斷其是否為 \(k\) 階滿樹,並輸出其 前序遍歷 序列。

注:樹中結點的度是其擁有的子樹的個數,而樹的度是樹內各結點的度的最大值。

輸入格式

輸入首先在第一行給出一個正整數 \(n\)\(\le 10^5\)),是樹中結點的個數。於是設所有結點從 \(1\)\(n\) 編號。

隨後 \(n\) 行,第 \(i\) 行(\(1\le i \le n\))給出第 \(i\) 個結點的父結點編號。根結點沒有父結點,則對應的父結點編號為 \(0\)。題目保證給出的是一棵合法多叉樹,只有唯一根結點。

輸出格式

首先在一行中輸出該樹的度。如果輸入的樹是 \(k\) 階滿樹,則加 1 個空格後輸出 yes,否則輸出 no。最後在第二行輸出該樹的前序遍歷序列,數字間以 1 個空格分隔,行首尾不得有多餘空格。

注意:兄弟結點按編號升序訪問。

測試資料

輸入樣例 1

7
6
5
5
6
6
0
5

輸出樣例 1

3 yes
6 1 4 5 2 3 7

輸入樣例 2

7
6
5
5
6
6
0
4

輸出樣例 2

3 no
6 1 4 7 5 2 3

解題思路

又是一個大水題。按照題意建樹,統計一下度數,維護一下最大度數(也就是整個樹的度),判斷一下度數不為0的節點中是否存在不等於最大度數的。

對於前序遍歷,甚至都不需要對每個節點的子節點進行排序(題中說兄弟結點按編號升序訪問),因為在按照題意建樹後,每個節點的子節點已經是嚴格遞增的了。故直接 \(dfs\) 一下輸出結果即可。

#include<bits/stdc++.h>
using namespace std;

#define ios ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
const int N = 1e5 + 10;

vector<int> e[N], ans;
int n, rt, mx, d[N];

void dfs(int u){
    ans.emplace_back(u);
    for(auto &v : e[u]) dfs(v);
}

int main(){
    ios;

    cin >> n;
    for(int v = 1, u; v <= n; v ++ ){
        cin >> u;
        if(!u) rt = v;
        else e[u].emplace_back(v), d[u] ++ ;
        mx = max(mx, d[u]);
    }

    bool flag = true;
    for(int i = 1; i <= n; i ++ )
        if(d[i] && d[i] != mx){
            flag = false;
            break;
        }
    cout << mx << ' ' << (flag ? "yes" : "no") << "\n";

    dfs(rt);

    for(int i = 0; i < n; i ++ ) cout << ans[i] << " \n"[i == n - 1];

    return 0;
}


L2-4 吉利矩陣

題目描述

所有元素為非負整數,且各行各列的元素和都等於 \(7\)\(3\times 3\) 方陣稱為“吉利矩陣”,因為這樣的矩陣一共有 \(666\) 種。

本題就請你統計一下,把 \(7\) 換成任何一個 \([2,9]\) 區間內的正整數 \(L\),把矩陣階數換成任何一個 \([2,4]\) 區間內的正整數 \(N\),滿足條件“所有元素為非負整數,且各行各列的元素和都等於 \(L\)”的 \(N\times N\) 方陣一共有多少種?

輸入格式

輸入在一行中給出 2 個正整數 \(L\)\(N\),意義如題面所述。數字間以空格分隔。

輸出格式

在一行中輸出滿足題目要求條件的方陣的個數。

測試資料

輸入樣例

7 3

輸出樣例

666

解題思路

賽時沒拿全分(我是不會告訴你我賽場上 \(dfs\) 寫不對然後直接寫了個 \(16\) 層迴圈然後每層加上一個提前判斷的)。

後來想想其實也是一個很典的搜尋 + 剪枝,可以用 \(row[i]\)\(col[i]\) 累加每行/列的和,直接回溯法。

第一個剪枝:在列舉值的時候限制一下行/列的和。

第二個剪枝:在列舉值的時候判斷一下每行/列剩下的未取值的位置都取 \(L\) 是否可以到達 \(L\)

\(dfs\)\((1, 1)\) 開始,假設當前的點為 \((x, y)\),如果遞迴到的 \((x, y)\)\(y\) 已經大於 \(N\) 了,此時需要進行換行,也就是 \(x = x + 1, y = 1\)

當到了最後一行且 \(y > N\) 時,此時整個方陣都搜尋完,判斷一下是否滿足要求,累計答案並停止搜尋即可。

#include<bits/stdc++.h>
using namespace std;

#define ios ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
const int N = 5;

int n, k, ans, row[N], col[N];

void dfs(int x, int y){
    if(x == n && y > n){
        for(int i = 1; i <= n; i ++ ) if(row[i] != k) return;
        for(int i = 1; i <= n; i ++ ) if(col[i] != k) return;
        ans ++ ;
        return;
    }
    if(y > n) x ++ , y = 1;
    for(int i = 0; row[x] + i <= k && col[y] + i <= k; i ++ ){ // 剪枝1 行/列和不超過k
        if(row[x] + i + k * (n - y) < k || col[y] + i + k * (n - x) < k) continue; // 剪枝2 可以達到k
        row[x] += i;
        col[y] += i;
        dfs(x, y + 1);
        row[x] -= i;
        col[y] -= i;
    }
}

int main(){
    ios;

    cin >> k >> n;
    
    dfs(1, 1);

    cout << ans << "\n";

    return 0;
}


L3-1 奪寶大賽

題目描述

奪寶大賽的地圖是一個由 \(n\times m\) 個方格子組成的長方形,主辦方在地圖上標明瞭所有障礙、以及大本營寶藏的位置。參賽的隊伍一開始被隨機投放在地圖的各個方格里,同時開始向大本營進發。

所有參賽隊從一個方格移動到另一個無障礙的相鄰方格(“相鄰”是指兩個方格有一條公共邊)所花的時間都是 1 個單位時間。但當有多支隊伍同時進入大本營時,必將發生火拼,造成參與火拼的所有隊伍無法繼續比賽。大賽規定:最先到達大本營並能活著奪寶的隊伍獲得勝利。

假設所有隊伍都將以最快速度衝向大本營,請你判斷哪個隊伍將獲得最後的勝利。

輸入格式

輸入首先在第一行給出兩個正整數 \(m\)\(n\)\(2<m,n\le 100\)),隨後 \(m\) 行,每行給出 \(n\) 個數字,表示地圖上對應方格的狀態:\(1\) 表示方格可透過;\(0\) 表示該方格有障礙物,不可通行;\(2\) 表示該方格是大本營。題目保證只有 1 個大本營。

接下來是參賽隊伍資訊。首先在一行中給出正整數 \(k\)\(0<k< \frac{m\times n}{2}\)),隨後 \(k\) 行,第 \(i\)\(1\le i\le k\))行給出編號為 \(i\) 的參賽隊的初始落腳點的座標,格式為 x y。這裡規定地圖左上角座標為 1 1,右下角座標為 n m,其中 n 為列數,m 為行數。注意參賽隊只能在地圖範圍內移動,不得走出地圖。題目保證沒有參賽隊一開始就落在有障礙的方格里。

輸出格式

在一行中輸出獲勝的隊伍編號和其到達大本營所用的單位時間數量,數字間以 1 個空格分隔,行首尾不得有多餘空格。若沒有隊伍能獲勝,則在一行中輸出 No winner .

測試資料

輸入樣例 1

5 7
1 1 1 1 1 0 1
1 1 1 1 1 0 0
1 1 0 2 1 1 1
1 1 0 0 1 1 1
1 1 1 1 1 1 1
7
1 5
7 1
1 1
5 5
3 1
3 5
1 4

輸出樣例 1

7 6

樣例 1 說明

七支隊伍到達大本營的時間順次為:7、不可能、5、3、3、5、6,其中隊伍 4 和 5 火拼了,隊伍 3 和 6 火拼了,隊伍 7 比隊伍 1 早到,所以獲勝。

輸入樣例 2

5 7
1 1 1 1 1 0 1
1 1 1 1 1 0 0
1 1 0 2 1 1 1
1 1 0 0 1 1 1
1 1 1 1 1 1 1
7
7 5
1 3
7 1
1 1
5 5
3 1
3 5

輸出樣例 2

No winner.

解題思路

由題意可知,只需要求出到達終點時 沒有其他隊伍到達 並且 距離最小 的隊伍編號即可。如果沒有滿足條件的隊伍,輸出 No winner

很明顯用 \(bfs\) 求最短距離即可。

如果每個隊伍都進行一次 \(bfs\) 計算到達終點的最短距離,顯然會超時。因此,可以反過來 \(bfs\) ,將大本營作為起點進行 \(bfs\),計算到其他各個隊伍所在位置的最短距離。

但是!這個題有個很噁心的地方,就是 “這裡規定地圖左上角座標為 1 1,右下角座標為 n m,其中 n 為列數,m 為行數。”

相當於這個地圖是豎著的。所給的隊伍的座標也是豎著的地圖對於的座標(真的很反人類,賽時我一直接異或了,樣例一直不過)。

故在讀入每個隊伍座標後,\(x\)\(y\) 反著存一下即可。其他就沒什麼了。

#include<bits/stdc++.h>
using namespace std;

#define ios ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
typedef pair<int, int> pii;
const int N = 110, M = 5010;
const int dx[4] = {1, -1, 0, 0}, dy[4] = {0, 0, 1, -1};

int n, m, q, sx, sy, g[N][N], d[N][N];
pii des[M];

void bfs(){
    queue<pii> q;
    q.emplace(sx, sy);
    d[sx][sy] = 0;

    while(q.size()){
        auto [x, y] = q.front();
        q.pop();

        for(int k = 0; k < 4; k ++ ){
            int nx = x + dx[k], ny = y + dy[k];
            if(nx < 1 || ny < 1 || nx > n || ny > m) continue;
            if(!g[nx][ny] || ~d[nx][ny]) continue;
            q.emplace(nx, ny);
            d[nx][ny] = d[x][y] + 1;
        }
    }
}

int main(){
    ios;

    cin >> n >> m;
    memset(d, -1, sizeof d);
    for(int i = 1; i <= n; i ++ )
        for(int j = 1; j <= m; j ++ ){
            cin >> g[i][j];
            if(g[i][j] == 2) sx = i, sy = j;
        }

    cin >> q;
    for(int i = 1, x, y; i <= q; i ++ ){
        cin >> x >> y;
        des[i] = {y, x};
    }

    bfs();

    unordered_map<int, int> cnt; // 每個距離有多少個隊伍
    for(int i = 1; i <= q; i ++ ){
        auto [x, y] = des[i];
        cnt[d[x][y]] ++ ;
    }

    int mn = 1e9, ans = -1;
    for(int i = 1; i <= q; i ++ ){
        auto [x, y] = des[i];
        if(d[x][y] == -1 || cnt[d[x][y]] > 1) continue;
        if(d[x][y] < mn) mn = d[x][y], ans = i;
    }

    if(ans == -1) cout << "No winner." << "\n";
    else cout << ans << ' ' << mn << "\n";

    return 0;
}


L3-2 工業園區建設

待更新



L3-3 攀巖

待更新



相關文章