SCNUOJ 2020 軟件學院 天梯賽選拔賽 + 藍橋杯熱身賽 題解

SCNUOJ 2020 軟院天梯賽選拔賽 + 藍橋杯熱身賽 題解


寫在前面

本次比賽面向軟件學院2017、2018、2019級學生,賽制為IOI賽制,時長3小時。

最終共有311名同學提交代碼,共有302名同學獲得有效分數。

最終前18名的同學(除去打星)入圍2020軟件學院團體程序設計天梯賽名單,其中最高分為183分,最低分為105分。

祝賀他們!

題目限制

題目 時間限制 空間限制
L1-1 400MS 64MB
L1-2 400MS 64MB
L1-3 1000MS 256MB
L1-4 1000MS 64MB
L1-5 1000MS 64MB
L1-6 1000MS 64MB
L1-7 1000MS 256MB
L1-8 1000MS 256MB
L2-1 1000MS 128MB
L2-2 1000MS 128MB
L2-3 1000MS 256MB
L2-4 1000MS 64MB
L3-1 2000MS 64MB

ps:一般來說,64MB內存足以。

L1-1 建議改成:空 格 體(5)


Problem Description

LXY 在某大型知名同性交友網站上傳了一個視頻,但并沒有想到有意思的標題。作為標題黨的 LXY 想讓博學多識的你給他想一個好標題。

結合 LXY 的視頻內容,你自然想到了一個非常好的標題。為了將自己的標題告訴 LXY,你需要在評論區用建議空格體加入標題競標。

建議空格體示例
Input

輸入共一行,一個由大小寫字母及數字組成的字符串(不超過 100000 個字符),表示你想出的標題。

Output

輸出一行,開頭是"Proposed change to: ",后面是標題并將標題內的每個字符用空格隔開。

Simple Input

114514

Simple Output

Proposed change to: 1 1 4 5 1 4

Source

CGY

思路

讀取字符串,按要求每一個字符前輸出一個空格即可。

這道題部分人用JAVA和C語言多次調用strlen函數會導致代碼TLE,因為C語言里strlen函數的時間復雜度為O(n),Java的String類比較慢,推薦使用StringBuffer來處理。

代碼
#include <bits/stdc++.h>
using namespace std;
int main(){
    string str;
    cin>>str;
    cout<<"Proposed change to:";
    for(int i = 0;i<str.size();++i) cout<<" "<<str[i];
    return 0;
}

L1-2 多邊形(5)


Problem Description

Veggie最近疲于教小侄子寫紅黑樹,連多邊形的內角和都忘記怎么求了,想請作為數學小天才的你幫忙編程解決這個問題。

Input

輸入在一行中給出一個整數n (3≤n≤10),表示多邊形的邊數。

Output

在一行中對應輸出n邊形內角和的值x,格式為”Answer: x degree”(不含雙引號)。

Simple Input

4

Simple Output

Answer: 360 degree

Source

LWH

思路

多邊形內角和 = (邊數 - 2) * 180

代碼
#include <stdio.h>
int main()
{
    int n;
    scanf("%d", &n);
    printf("Answer: %d degree", (n - 2) * 180);
    return 0;
}

L1-3 米斯達與數字4(10)


Problem Description

Guīdo Misuta是《JOJO的奇妙冒險:黃金之風》里面的人物,他對于數字4很是在意,他認為4是十分不吉利的數字。現在有一串數字,請問這串數字迫害米斯達的次數是多少?

迫害次數=數字4出現的次數+數字長度內4出現的次數。

//性感手槍警告

Input

輸入一串數字,長度不超過200位

Output

數字4出現的次數和數字長度中4的次數的總和

Simple Input

1234

Simple Output

2

Hint

數字中出現4的次數為1,同時數字長度為4,因此總和為1+1=2

Source

LXY

思路

這道題分為兩個部分來求解,一個是求這串數字中4的個數,另一個是這串數字位數(長度)中4的個數。
在這里,我們可以采用string字符串來讀入,借助string.size()函數來快速得到這串數字的位數,然后將位數不斷模10整除10來獲得每一位的數字并統計其中4的個數。最后遍歷這個字符串,判斷字符為'4'的個數,兩部分累加即為答案。

代碼
#include <bits/stdc++.h>
using namespace std;
int main(){
    string str;
    cin>>str;
    int n = str.size();
    int cnt = 0;
    while(n){
        if(n%10 == 4) cnt++;
        n/=10;
    }
    for(int i = 0;i<str.size();++i){
        if(str[i]=='4') cnt++;
    }
    cout<<cnt;
    return 0;
}

L1-4 打印(10)


Problem Description

真的是道簡單打印題哈——給定一個整數數組nums,請打印其中位數為偶數且數值也為偶數的數字。

Input

第一行為一個整數N(1 \leq N \leq 10),代表數組nums有N個整數。

接下來有N行,每行有一個整數A(-2^{30} \leq A \leq 2^{30})

Output

若輸入的A的位數為偶數且數值也為偶數,則打印該行。

Simple Input1

3
12
2
13

Simple Output1

12

Simple Input2

2
-1
-12

Simple Output2

-12

Source

SLF

思路

方法一:用字符串處理,判斷字符串位數是否為偶數(負號不算入內),再判斷最后一個字符是否為偶數即可。

方法二:直接讀入整數處理。

代碼(方法一)
#include <bits/stdc++.h>
using namespace std;
int main(){
    string str;
    int n;
    cin>>n;
    while(n--){
        cin>>str;
        int num_len = str.size();
        if(str[0]=='-'){
            num_len--;
        }
        if(str[str.size()-1]%2==0 && num_len%2==0){
            cout<<str<<endl;
        }
    }
    return 0;
}

L1-5 尋找連座(15)


Problem Description
image.png

軟件學院的LXY和CGY要去一起坐火車外出比賽。火車上有許多節車廂,每節車廂的每一排都有4個座位,且這4個座位被過道分成了兩半。不幸的是,當LXY和CGY到了車上時,一些位子已經有人了。

但LXY和CGY是好基友,于是他們想要找一對連在一起的座位,以便于更好地打游戲(手動劃掉)學習。一對連在一起的座位指得是,這兩個座位在同一排且不被過道分割開。已知火車上每節車廂上的座位情況,請問能否找得到這樣的一對連座?

Input

第一行為一個整數N(1≤N≤100),代表火車總長度。

緊接著輸入N行,每行由五個字符組成,代表一排座位或者車廂分割線。

若是一排座位,第1、2、4、5個字符分別代表一排中的4個座位情況,字符'O'表示空座位,字符'X'表示座位上已經有人,即被占用。第3個字符為'|',代表過道。

若是車廂分割線,則五個字符均為'-'。

數據保證每節車廂至少有一排座位。

Output

如果能夠找到這樣一組連座,請先輸出一行字符串"Yes"(不輸出引號),接下來一行,輸出一個整數k,代表這組連座在第k車廂(車廂編號由1開始,從前往后編號遞增)。

接下來輸出火車的座位情況,將LXY和CGY坐的連座用"+"來表示。

若不能找到這樣的連座,則輸出一行字符串"No"(不輸出引號)。

有多組座位符合要求時,將LXY和CGY安排在靠前的車廂,靠前的排,如果同一排還有兩組可行解,選擇將LXY和CGY排在左邊。例如:

OO|OO                ++|OO

-----        ->      -----

OO|XX                OO|XX
Simple Input1

6
OO|OX
XO|XX
OX|OO
XX|OX
-----
OO|XX

Simple Output1

Yes
1
++|OX
XO|XX
OX|OO
XX|OX
-----
OO|XX

Simple Input2

6
XO|OX
XO|XX
OX|XO
XX|OX
-----
OX|XX

Simple Output2

No

Source

SLF

思路

字符串處理題。

遍歷字符串數組,判斷出LXY和CGY連座的位置,記錄下來即可。

需要注意得是,車廂分割線代表著前一節車廂和后一節車廂的分割,可以聯想火車/高鐵的平面示意圖。有些同學沒有注意到這一點,導致Wrong Answer。

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

string str[110];

int main(){
    int n;
    cin>>n;
    for(int i = 0;i<n;++i){
        cin>>str[i];
    }
    int cnt = 1;
    int ansi,ansj;
    int flag = false;
    for(int i = 0;i<n;++i){
        for(int j = 0;j<4;++j){
            if(str[i][j]=='-'){
                ++cnt;//統計當前車廂為第幾車廂
                break;
            }
            if(str[i][j]=='O' && str[i][j+1]=='O'){
                flag = true;
                ansi = i,ansj = j;
                break;
            }
        }
        if(flag) break;
    }
    if(flag){
        cout<<"Yes"<<endl;
        cout<<cnt<<endl;
        for(int i = 0;i<n;++i){
            for(int j = 0;j<5;++j){
                if(i==ansi&&j==ansj){
                    cout<<"++";
                    ++j;
                    continue;
                }
                cout<<str[i][j];
            }
            cout<<endl;
        }
    }else{
        cout<<"No"<<endl;
    }
    return 0;
}

L1-6 Heroes never die(15)


Problem Description
image.png

OverWatch里的三個英雄有著上圖的克制關系。

現在,我們需要寫一個程序來自動選擇英雄,以對抗對方選擇的英雄。

選擇規則如下:

1、當回合數是K的倍數時,選擇一個相同的英雄。

2、當回合數是N的倍數時,選擇一個”天使”英雄,并在接下來一行輸出” Heroes never die”。

3、當回合數是K和N的倍數時,選擇一個相同的英雄。

4、其他回合,選擇一個克制對方英雄的英雄。

Ps:回合數從1開始

Input

首先第一行給出兩個整數K、N(1≤K,N≤10),由空格隔開。隨后每行給出一個對方選擇的英雄:”Mccree”代表”麥克雷”,”Hanzo”代表”半藏”,”Genji”代表源氏。End代表輸入結束,這一行不要做任何處理。

數據保證:回合數最多不超過2000

Output

參照題意的規則,每次選擇一個英雄,單獨輸出一行。其中”Mercy”代表”天使”,選擇”天使”時,接下來一行要接著輸出”Heroes never die”。(所有輸出均不帶雙引號)

Simple Input1

2 3
Mccree
Hanzo
Genji
Genji
Hanzo
Mccree
End

Simple Output1

Genji
Hanzo
Mercy
Heroes never die
Genji
Mccree
Mccree

Source

SLF

思路

判斷每一回合是否為K或者N的倍數,然后按照題意要求輸出即可。

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

int main(){
    string Mccree = "Mccree",Hanzo = "Hanzo",Genji = "Genji";
    int n,k,cnt = 0;
    cin>>k>>n;
    while(true){
        cnt++;
        string str;
        cin>>str;
        if(str == "End") break;
        if(cnt%k==0){
            cout<<str;
        }else if(cnt%n==0){
            cout<<"Mercy"<<endl;
            cout<<"Heroes never die";
        }else{
            if(str == Mccree) cout<<Genji;
            else if(str == Hanzo) cout<<Mccree;
            else              cout<<Hanzo;
        }
        cout<<endl;
    }
    return 0;
}

L1-7 LXY的小機器人(20)


Problem Description

LXY上大三了,他選修了機器人技術這門課。期末的大作業是要寫一段機器人的控制程序,完成某些功能。LXY可以對機器人發布WSAD四個指令,在二維平面上,指令分別表示:

W:機器人向上走一步(X,Y+1)

S:機器人向下走一步(X,Y-1)

A:機器人向左走一步(X-1,Y)

D:機器人向右走一步(X+1,Y)

LXY的程序會從頭到尾循環的運行,LXY設定的每個指令會在1s內完成,現在LXY想知道經過一段時間后,機器人所處在的二維平面上的位置是多少。(LXY開始時已經將機器人放在(0,0)點)

Input

第一行輸入一串由‘W’,‘S’,‘A’,‘D’組成的字符串,表示指令(指令長度不超過2000)
第二行輸入一個數字N,表示LXY想知道經過N秒后機器人的位置(N<=2000000000)

Output

輸出包括兩個數字,表示經過N秒后機器人的坐標位置,分別為橫坐標和縱坐標

Simple Input1

WSDDWSDAADW
12

Simple Output1

2 2

Source

LXY

思路

模擬題,但要注意數據范圍,數據N <= 2* 10^{9}

所以若直接暴力模擬,時間復雜度為O(N),在一秒內無法完成。這里有許多同學沒有注意到這一點,導致無法通過最后兩個測試點。

然后我們可以分析可得,完成一次指令周期機器人相對位置變化是固定的,而指令周期len最長只有2000,時間復雜度O(len)是可以接受的。

于是我們可以先算出一個指令周期的相對位置,然后乘以(N/len)次,最后我們再模擬一次(N/len)的余數部分即可。

代碼
#include <bits/stdc++.h>
using namespace std;
int main(){
    string str;
    cin>>str;
    int x = 0,y = 0;
    for(int i = 0;i<str.size();++i){
        if(str[i]=='W') ++y;
        if(str[i]=='S') --y;
        if(str[i]=='A') --x;
        if(str[i]=='D') ++x;
    }
    int len;
    cin>>len;
    x *= (len/str.size());y *= (len/str.size());
    for(int i = 0;i<(len%str.size());++i){
        if(str[i]=='W') ++y;
        if(str[i]=='S') --y;
        if(str[i]=='A') --x;
        if(str[i]=='D') ++x;
    }
    cout<<x<<" "<<y;
    return 0;
}

L1-8 LXY的圓和矩形(20))


Problem Description

LXY上初中了,數學老師有一天留下了一個作業。已知圓上有N個點,給出每個點之間的弧長(為方便)輸入,給出的弧長已經除了π,請問這些點一共能構成多少個不重復的矩形。

Input

輸入第一行整數N(N<=100),第二行輸入N個整數,表示相鄰每兩個點之間的弧長。

Output

輸出N個點所構成的不重復的矩形個數

Simple Input1

8
1 2 2 3 1 1 3 3

Simple Output1

3

Hint
image.png

據說,直徑所對的弦為半圓;

而且,直徑所對的圓周角為直角;

那么問題來了,矩形有幾個直角呢?

Source

LXY

思路

圓內一條直徑對應的兩個圓周角均為直角,一個矩形有四個直角,所以兩條直徑確定一個矩形。

我們計算出圓內的點能連成多少條直徑,然后兩兩直徑算一次即可得出矩形數量。

如果兩個點連接的直線對應的弧長正好為總弧長的一半,則該直線為圓的直徑。

ps:只要兩個矩形四點任意一點不同就視為不同的矩形。這里有一些同學可能考慮復雜了。

代碼
#include <bits/stdc++.h>
using namespace std;
int a[110];
int main(){
    int n,sum = 0,m = 0;
    cin>>n;
    for(int i = 1;i<=n;++i){
        cin>>a[i];
        sum += a[i];
    }
    for(int i = 1;i<=n;++i){
        int t = a[i];
        for(int j = i+1;j<=n-1;++j){
            t += a[j];
            if(t*2 == sum){
                ++m;
                break;
            }
        }
    }
    int res = 0;
    for(int i = 1;i<=m;++i){
        res+=(i-1);
    }
    cout<<res;
    return 0;
}

L2-1 竊取密碼(25)


Problem Description

LWH想要竊取ACM-ICPC系統的管理員密碼,以提前查看今年World Final的題目。為此,他偷偷溜進管理員辦公室,偷了一張紙,上面有一張清單,清單上有著n個密碼,密碼均由小寫的英文字母組成。管理員密碼就在其中。
LWH回家后,開始準備對系統進行黑客攻擊。為了破解系統,他不得不輸入一個個密碼進行嘗試。不過幸運得是,他發現了這些密碼具有等效性。

  • 如果清單上兩個密碼a和b都包含同一個字母,則密碼a等效于b

  • 如果清單上兩個密碼a和b都等效于c,則a也等效于b

如果管理員密碼與某個密碼等效,則用戶也可以使用等效的密碼來登錄系統。

例如,如果清單列表包含密碼”a”, ”b”, ”ab”, ”d”,則”a”, ”b”, ”ab”彼此等效,但密碼”d”不與清單列表中任何的密碼等效。所以:

  • 如果管理員的密碼為”b”,那么您可以使用以下任何密碼訪問系統:”a”, ”b”, ”ab”。

  • 如果管理員的密碼為”d”,那么您只能使用”d”來訪問系統。

清單列表中只有一個密碼是測試系統的管理員密碼。請幫助LWH計算能正確進入系統所需的最小密碼數。請記住,LWH不知道清單列表中哪個密碼是管理員的密碼。

Input

第一行包含一個整數n(1≤n≤2*10^5),代表列表清單中的密碼數。

接下來n行,每行一個非空字符串s_i代表密碼,s_i長度不超過50個字符。某些密碼可能一樣。

數據保證所有字符串總長度不超過10^6,所有密碼都由小寫英文字母組成。

Output

輸出一個整數,代表需要測試密碼的最小數量,其中這些密碼能夠確保LWH進入系統。

Simple Input1

4
a
b
ab
d

Simple Output1

2

Hint

第一個樣例LWH只需要測試兩個密碼即可,一個密碼為”a”、”b”、”ab”中的任意一個,因為它們相互具有等效性,另一個密碼為”d”,所以輸出2。

Source

SLF

思路

題意要求算可以將給出的密碼列表分成多少類,同一類內的密碼相互等效。

我們可以用并查集來解決這個問題。

1、設置一個長度為26的整數數組,分別代表26個英文字母指向哪一個字符串。

2、遍歷每一個字符串的每一個字符。

3、若該字符的數組尚未指向,這將該字符指向這個字符串。

4、若該字符的數組已指向,這將該字符串用并查集指向該字符已指向的字符串。

5、最后統計有多少個字符串指向自己即可。(在并查集中,每個字符串一開始均指向自己)

ps:如果寫得并查集常數較大或者沒有進行路徑壓縮,只能拿到60%的分數。暴力查找求解也可以通過部分的測試點,但是代碼容易寫錯且分數不高。

代碼
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 200010;
int f[MAXN];
int find_father(int x){return f[x]==x?x:f[x]=find_father(f[x]);}
int main(){
    ios::sync_with_stdio(false);cin.tie(0);
    int flag[110];
    memset(flag,0,sizeof(flag));
    int n;
    cin>>n;
    for(int i = 1;i<=n;++i){
        f[i] = i;
        string str;
        cin>>str;
        for(int j = 0;j<str.size();++j){
            int t = str[j]-'a';
            if(!flag[t]){
                flag[t] = i;
            }
            else{
                int tx = find_father(flag[t]);
                int ty = find_father(i);
                if(tx!=ty){
                    f[tx] = ty;
                }
            }
        }
    }
    int res = 0;
    for(int i = 1;i<=n;++i){
        if(i==find_father(i))
            res++;
    }
    cout<<res;
    return 0;
}

L2-2 愧疚往事(25)


Problem Description

Veggie想起高中注冊時幫了初中語文老師插隊交學費,至今內心都因為那次插隊感到無比愧疚。為了能夠讓這件愧疚往事成為教訓,Veggie決定通過建立插隊模型來體會那些被插隊者的心情。在模型中,每個人都屬于且僅屬于一個朋友圈,圈內的人相互之間都是朋友。他們在排隊時,會在隊伍從后往前找熟人,如果找到了自己的朋友,就排在朋友后面,否則才會選擇排到隊伍最后。Veggie正在為找實習而忙碌,于是請你幫忙編程實現這個模型。

Input

輸入第一行中給出兩個整數N、M,其中N(1 \leq N \leq 100)朋友圈個數,M(1 \leq M \leq 10^5)是指令條數。

隨后N行中,每行給出一個朋友圈的信息。首先是整數K(1 \leq K \leq 10^3),表示這個朋友圈中的人數,接著是K個人員的編號(00000~99999),中間用空格分開。

隨后M行中,每一行給出一條模擬指令,指令有以下兩種形式:

"IN x": 表示編號為x的人員進入隊伍。

"OUT": 表示在隊首的人離開隊伍,此時我們需要輸出該人員的編號。
(注意:指令不含雙引號)

Output

按順序輸出離開隊伍的人員編號,每個人的編號占一行。如果對空的隊伍使用“OUT”指令,則輸出”EMPTY”(不含雙引號)。

Simple Input1

3 16
3 00001 00002 00003
3 11111 22222 33333
5 05030 05116 05103 05139 05044
IN 00001
IN 11111
IN 05030
IN 00002
IN 22222
OUT
OUT
OUT
OUT
OUT
IN 00003
IN 05103
IN 05139
IN 00001
OUT
OUT

Simple Output1

00001
00002
11111
22222
05030
00003
00001

Source

LWH

思路

隊列模擬題。

為了處理方便,我們將每一個朋友圈編號,然后用Map<string,int>來映射每一個人對應的朋友圈編號。(不用Map,也可以用數組代替相應功能)

然后我們設置一個編號隊列T,用來存放正在排隊的朋友圈編號。

最后我們給每一個朋友圈都再單獨設置一個隊列Q。

然后開始模擬:

當一個人x想入隊時,我們先判斷x所在的朋友圈隊列Q是否為空。

  • 若為空證明總隊列中沒有x的朋友,所以將x入隊到Q中,同時將x所對應的朋友圈編號i入隊到編號隊列T中。

  • 若不為空證明總隊列中有x的朋友,所以將x入隊到Q中即可。

當遇到出隊命令時,我們先判斷編號隊列T的隊首元素i,然后將i對應的隊列Q的隊首出隊.然后判斷Q是否為空,是則將隊列T隊首元素i進行出隊處理.

要注意處理遇到出隊命令時,需要判斷隊列是否為空,否則會造成Runtime Error。

所有入隊/出隊操作的時間復雜度都是O(lg(K*N))。

有許多同學直接暴力模擬題意,而沒有考慮到數據范圍和算法的時間復雜度,造成Time Limit。

這道題對Java選手不太友好,因為Java讀入和自帶的數據結構庫比較慢,算法正確的前提下可能會導致后面幾個點TLE。

代碼
#include <iostream>
#include <cstdio>
#include <string>
#include <queue>
using namespace std;
const int maxn = 1e5+10;
queue<int> q_id;
queue<int> q_member[1005];
int mapping[maxn], isInQueue[1005];
int n, m, k, t;
string str;

int main()
{
    scanf("%d %d", &n, &m);
    for (int i = 1; i <= n; i++) {
        scanf("%d", &k);
        for (int j = 0; j < k; j++) {
            scanf("%d", &t);
            mapping[t] = i;
        }
    }

    while (m--) {
        cin >> str;
        if (str == "IN") {
            scanf("%d", &t);
            if (!isInQueue[mapping[t]]) {
                isInQueue[mapping[t]] = 1;
                q_id.push(mapping[t]);
            }
            q_member[mapping[t]].push(t);
        } else {
            if (q_id.empty()) {
                printf("EMPTY\n");
                continue ;
            }
            int id = q_id.front();
            printf("%05d\n", q_member[id].front());
            q_member[id].pop();
            if (q_member[id].empty()) {
                q_id.pop();
                isInQueue[id] = 0;
            }
        }
    }
    return 0;
}

L2-3 關心員工(25)


Problem Description

SLF是G市某集團的總裁,他認為能一起打拼的員工都是好兄弟,因此他十分關心公司各個層次員工的生活水平。在公司中,每位員工都有且只有一名直屬上司(SLF除外),而上司有可能有多個下屬員工,于是各級員工之間就有了層次之分。為了更好地給員工補貼,SLF會定期統計各層次員工的平均收入。由于SLF忙于處理集團事務,于是想請你幫忙編程解決這個問題。

Input

輸入第一行中給出一個整數N(1 \leq N \leq 10^5),即整個公司的總人數,每個員工從1到N編號,總裁SLF的編號為1。隨后N行,第i行給出編號為i的員工的相關信息,格式如下:

K \quad N_1 \quad N_2 \quad ... \quad N_K \quad Salary

其中K是一個整數(0 \leq K \leq20),表示該員工直接下屬員工的個數,N_i是直接下屬員工的編號,Salary是一個正數(Salary \leq 2*10^4),表示該員工的收入。

Output

在一行中對應輸出公司的層次數m,隨后m行中輸出各層次員工的平均工資,格式為”Level i: average_i”(不含雙引號),其中average_i表示第i層員工的平均工資,需要保留兩位小數。

Simple Input1

7
2 2 3 100
3 4 5 6 2
1 7 3
0 4
0 5
0 6
0 7

Simple Output1

3
Level 1: 100.00
Level 2: 2.50
Level 3: 5.50

Source

LWH

思路

對多叉樹進行層序遍歷,計算每一層員工的平均和有多少層,最后輸出即可。

這里有個注意的點是:題目中的工資Salary是正數,而不是整數,所以輸入的變量要設置為浮點數。

這個點許多同學沒有注意到從而造成Wrong Answer,真是令人可惜。這也表明了同學們讀題能力稍顯薄弱,有待加強啊。

//你要問題意為什么這么坑?沒辦法,天梯賽的真題就有這樣坑人的。(手動滑稽)

代碼
#include <iostream>
#include <cstdio>
#include <vector>
#include <queue>

using namespace std;
const int maxn = 1e5 + 10;
vector<int> tree[maxn];
double num[maxn], salary;
int n, m, root, t;

void bfs(int root) {
    //pre_level表示當前層數,node_num表示本層的節點數
    int pre_level = 1, node_num = 0;
    //本層的平均值
    double avg = 0;
    vector<double> ans;
    queue<pair<int, int> > q;
    q.push(make_pair(root, 1));
    while (!q.empty()) {
        int node = q.front().first;
        int level = q.front().second;
        q.pop();
        //當去到新的一層就統計上一層的數據
        if (level != pre_level) {
            ans.push_back(avg / node_num);
            node_num = 0;
            avg = 0;
            pre_level = level;
        }
        //數量和工資的累計
        node_num++;
        avg += num[node];
        //將所有的子節點放入隊列中
        for (int i = 0; i < tree[node].size(); i++) {
            q.push(make_pair(tree[node][i], level + 1));

        }
    }
    //由于最后一層無法再到達新的一層,所以要單獨計算
    ans.push_back(avg / node_num);

    //輸出結果
    printf("%d\n", pre_level);
    for (int i = 0; i < ans.size(); i++) {
        printf("Level %d: %.2f\n", i + 1, ans[i]);
    }
}

int main()
{
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> m;
        for (int j = 0; j < m; j++) {
            cin >> t;
            tree[i].push_back(t);
        }
        cin >> salary;
        num[i] = salary;
    }
    bfs(1);
    return 0;
}

L2-4 奇怪的需求增加了.jpg(25)


Problem Description

自從復工以來,CGY 一直在接一些奇怪的需求,需求多到做都做不完(他急了,他急了)。由于 CGY 是單核單線程處理器(Intel 賽揚 B720),所以他每次只能同時做一個需求。現在 CGY 手上堆了 N 個需求,每個需求都需要一段時間完成,如果他在一定時間內沒有完成某個需求,那么這個需求就會被他的師兄憶兔之心拿過去做,這樣憶兔之心就有可能抓到 CGY 把之前趕需求的時間都用來打雀魂(而且還上不了雀圣)的把柄。為了防止被發現,CGY 需要完成盡可能多的需求,那你能幫幫他嗎?

9c551c53ca.png
Input

第一行是一個整數 N(N <= 150000)

接下來 N 行每行兩個整數 T_1T_2,描述了一個需求:完成這個需求需要 T_1 小時,如果在 T_2 小時之內還沒有完成(如果剛好在 T_2 時刻完成也算完成,踩點不算遲到嘛),這個需求就被撈走了。(數據保證 T_1 <= T_2 <= 1e15

Output

輸出一個整數 S,表示最多可以完成 S 個需求。

Simple Input1

4
100 200
200 1300
1000 1250
2000 3200

Simple Output1

3

Source

CGY

思路

堆+貪心。

我們可以先按照T_{2}進行升序排序,然后遍歷這些需求。遍歷的同時維護一個nowtime(代表現在的時刻),如果當前需求能夠被滿足,就將這個需求加入到優先隊列PQ(其中PQ是T_{1}的大根堆)。如果當前需求不能被滿足,則判斷當前需求能否替換掉PQ頂端的需求,可以的話就替換,另外需要注意替換后nowtime可能會發生變化。這樣替換并不會造成錯誤,因為我們已經對需求進行排序,所以替換對PQ中的其他需求不會產生沖突。

代碼
#include <bits/stdc++.h>

using namespace std;

int n;
pair<long long, long long> needs[150010];
priority_queue<long long> pq;
int ans;
long long last;

int main(void)
{
    ios::sync_with_stdio(false);
    cin.tie(0);

    cin >> n;
    for (int i = 0; i < n; ++i) {
        cin >> needs[i].second >> needs[i].first;
    }
    sort(needs, needs + n);
    for (int i = 0; i < n; ++i) {
        if (last + needs[i].second <= needs[i].first) {
            ++ans;
            last += needs[i].second;
            pq.push(needs[i].second);
        } else if (!pq.empty() && needs[i].second < pq.top()) {
            last += needs[i].second - pq.top();
            pq.pop();
            pq.push(needs[i].second);
        }
    }
    cout << ans << endl;

    return 0;
}

L3-1 《還債啦!動物森友會》(30)


Problem Description

Dr.Su 每天辛苦工作,缺乏休息時間,只能深夜在夢里玩動物之森聊以慰藉。在夢里,帶資本家貍吉還是那個貍吉,西施惠還是那個西施惠,該還的貸款還是要還。于是,Dr.Su 給夢里的這個版本起名叫《還債啦!動物森友會》。

在《還債啦!動物森友會》里,賺錢的唯一方法是——從地上收集樹枝賣錢(這才是真正的里數挑戰)。這次的無人島是一個 N × M 的長方形島嶼(難道哪一次不是嗎?),第 ij 列的格子內可以收集到 C_{ij} 個樹枝。Dr.Su 只會向上下左右四個方向走,并且不能離開這座無人島(離開了貍吉會把你綁回來繼續打工的),每當他走到一個格子,就會花費一個單位的時間將格子內的樹枝全部收集完。眾所周知,這是一個神(keng)奇(die)的島嶼,所以當 Dr.Su 收集完并離開某一格子后,該格子會重新刷新出原來所有的樹枝。

Dr.Su 建的房子(房子所在的格子沒有樹枝)在第 AB 列,他從這里出發,花費 K 單位的時間收集樹枝,且 K 單位時間過后他恰好回到自己的房子,請問他最多能采集多少樹枝?

(什么?你問 Dr.Su 這么聰明為什么不自己算?有動森玩誰還做題啊!)

微信圖片_20200328141305.jpg
Input

第一行共五個正整數,分別是行數 N(1 <= N <= 100)、列數 M(1 <= M <= 100)、房子位置 A(1 <= A <= N)B(1 <= B <= M) 和時間 K(K <= 1e9),保證 K 是偶數。

接下來 N 行每行 M 個非負整數 C_{ij}(0 <= C_{ij} <= 1e9),表示對應位置樹枝的數量。且保證 C_{AB} = 0

Output

一個整數,表示 Dr.Su 能采集到的最多的樹枝數。

Simple Input1

2 2 1 1 2
0 1
2 1

Simple Output1

2

Simple Input2

2 2 1 1 4
0 5
5 10

Simple Output2

20

Simple Input3

3 3 2 2 6
5 1 0
1 0 3
1 3 3

Simple Output3

15

Source

CGY

思路

登頂題。

首先,Dr.Su的路徑可以視作原路返回。因為路徑采摘樹枝數sum = 去時采摘的樹枝數x + 回時采摘的樹枝數y,如果x是最優的x,那么想要sum最大,那么y必定 = x。所以我們可以將時間K/2,只需要算x,最后答案 = x*2。

所以我們可以進行搜索處理,但由于時間K最大有1e9,所以會超時。

其次,我們分析可得,如果K/2 > n*m,那么Dr.Su會在最后一定會轉圈圈,而且圈的周長等于2,也就說Dr

.Su會在兩個格子之間反復橫跳。因為我們可以假設如果圈的周長 = n,那么這n個格子里面一定可以選出兩個格子使得這兩個格子的樹枝數/2 >= n個格子的樹枝數/n。

所以如果K/2 > n*m,那么我們只用對全部格子記憶化搜索n*m次即可。

代碼一
#include <bits/stdc++.h>

using namespace std;

#define INF 0x7f7f7f7f

long long n;
long long m;
long long a;
long long b;
long long k;
long long c[110][110];
long long dp[110][110][2];
long long ans;
long long dx[4] = {-1, 0, 1, 0};
long long dy[4] = {0, 1, 0, -1};
long long step;
int tag = 0;

bool check(int x, int y) {
    return !(x < 1 || y < 1 || x > n || y > m);
}

int main(void)
{
    ios::sync_with_stdio(false);
    cin.tie(0);

    cin >> n >> m >> a >> b >> k;
    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= m; ++j) {
            cin >> c[i][j];
            dp[i][j][0] = dp[i][j][1] = -INF;
        }
    }
    k /= 2;
    step = min(k, n * m);
    dp[a][b][0] = 0;
    for (int i = 1; i <= step; ++i) {
        tag ^= 1;
        for (int row = 1; row <= n; ++row) {
            for (int col = 1; col <= m; ++col) {
                long long maxp = -INF;
                long long maxc = -INF;
                for (int dir = 0; dir < 4; ++dir) {
                    int newx = row + dx[dir];
                    int newy = col + dy[dir];
                    if (check(newx, newy)) {
                        maxp = max(maxp, dp[newx][newy][1 ^ tag]);
                        maxc = max(maxc, c[newx][newy]);
                    }
                }
                if (maxp != -INF) {
                    dp[row][col][tag] = maxp + c[row][col];
                    ans = max(ans, dp[row][col][tag] * 2 - c[row][col] + (maxc + c[row][col]) * (k - i));
                }
            }
        }
    }
    cout << ans << endl;

    return 0;
}
代碼二(ZLM大師37ms的代碼orz)
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <queue>

const int dir_x[] = {0, 0, -1, 1};
const int dir_y[] = {-1, 1, 0, 0};
const int MAXN = 101;

long long dp[MAXN][MAXN], ss[MAXN][MAXN];
int s[MAXN][MAXN], step[MAXN][MAXN];

int main() {
    int n, m, a, b, k;
    memset(dp, 0xFF, sizeof(dp));
    scanf("%d%d%d%d%d", &n, &m, &a, &b, &k);
    k >>= 1;
    for (int i = 1; i <= n; ++i)
        for (int j = 1; j <= m; ++j)
            scanf("%d", s[i] + j);
    memset(ss, 0, sizeof(ss));
    for (int x = 1; x <= n; ++x)
        for (int y = 1; y <= m; ++y) {
            for (int i = 0; i < 4; ++i) {
                int xx = x + dir_x[i];
                int yy = y + dir_y[i];
                if (!xx || xx > n || !yy || yy > m) continue;
                ss[x][y] = std::max(ss[x][y], (long long)s[x][y] + s[xx][yy]);
            }
        }
    long long ans = 0;
    std::queue< std::pair< int, int > > q;
    q.push(std::make_pair(a, b));
    dp[a][b] = 0;
    step[a][b] = 0;
    while (!q.empty()) {
        int x = q.front().first, y = q.front().second;
        q.pop();
        if (step[x][y] > k) break;
        long long left = k - step[x][y];
        left *= ss[x][y];
        ans = std::max(ans, (dp[x][y] << 1) + left - s[x][y]);
        for (int i = 0; i < 4; ++i) {
            int xx = x + dir_x[i];
            int yy = y + dir_y[i];
            if (!xx || xx > n || !yy || yy > m) continue;
            long long tmp = dp[x][y] + s[xx][yy];
            if (dp[xx][yy] == -1 || dp[xx][yy] + ((step[x][y] + 1 - step[xx][yy]) >> 1) * ss[xx][yy] < tmp) {
                dp[xx][yy] = tmp;
                step[xx][yy] = step[x][y] + 1;
                q.push(std::make_pair(xx, yy));
            }
        }
    }
    printf("%lld\n", ans);
    return 0;
}

總結

這次比賽的前10名榜單每隔幾分鐘就會發生變化,競爭非常激烈。

這套題目的Lv1難度相對較低,所以主要拉開差距的題目在于Lv2數據結構方面的題目,這也和天梯賽現場賽題目的難度分布相同。如果同學們感覺做Lv2數據結構的題目稍顯吃力,表明數據結構這方面有待加強。

無論結果如何,盡了自己最大的努力便是成功。沒有入圍隊伍名單的同學不用氣餒,好好準備接下來的藍橋杯等賽事吧~

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,461評論 6 532
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,538評論 3 417
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,423評論 0 375
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,991評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,761評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,207評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,268評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,419評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,959評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,782評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,983評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,528評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,222評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,653評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,901評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,678評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,978評論 2 374

推薦閱讀更多精彩內容