2020年第十一屆藍橋杯國賽 B組個人題解


試題下載

試題 A: 美麗的 2

本題總分:5 分
【問題描述】
小藍特別喜歡 \(2\),今年是公元 \(2020\) 年,他特別高興。
他很好奇,在公元 \(1\) 年到公元 \(2020\) 年(包含)中,有多少個年份的數位中 包含數字 \(2?\)
【我的題解】
數位分離,水題了。循環 \(1\)\(2020\),寫個函數判斷一下就好了

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <map>
#include <set>
#include <queue>

using namespace std;
typedef long long LL;
typedef pair<int, int> PII;

bool hasDigitTwo(int number) {
    while (number) {
        if (number % 10 == 2) {
            return true;
        }
        number /= 10;
    }
    return false;
}

int main() {
    int answer = 0;
    for (int i = 1; i <= 2020; i++) {
        if (hasDigitTwo(i)) {
            answer++;
        }
    }
    printf("%d\n", answer);
    return 0;
}

我的答案 \(563\)

試題 B: 擴散

本題總分:5 分
【問題描述】
小藍在一張無限大的特殊畫布上作畫。
這張畫布可以看成一個方格圖,每個格子可以用一個二維的整數坐標表示。
小藍在畫布上首先點了一下幾個點:(0,0), (2020,11), (11,14), (2000,2000)。 只有這幾個格子上有黑色,其它位置都是白色的。
每過一分鍾,黑色就會擴散一點。具體的,如果一個格子里面是黑色,它 就會擴散到上、下、左、右四個相鄰的格子中,使得這四個格子也變成黑色 (如果原來就是黑色,則還是黑色)。
請問,經過 \(2020\) 分鍾后,畫布上有多少個格子是黑色的。
【我的題解】
一開始寫了個按層遍歷的 \(bfs\) , 發現運行不出結果,然后換了思路。將題目換種問法,求無限大的畫布里與給出的四個點中至少一個點曼哈頓距離小於等於 \(2020\) 的點有多少個。那么就可以想到, \(x\) 坐標小於 \(-3000\) 或者大於 \(5000\) 的點不可能滿足這個條件,同理 \(y\) 坐標小於 \(-3000\) 或者大於 \(5000\) 的點也不可能滿足這個條件。那么無限大的畫布就有范圍了,枚舉這個范圍內的點,統計有多少個滿足條件的就好了。

#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <map>
#include <set>
#include <queue>

using namespace std;
typedef long long LL;
typedef pair<int, int> PII;

const PII POINTS[] = {{0, 0}, {2020, 11}, {11, 14}, {2000, 2000}};

bool isBlack(int x, int y, int i) {
    int disX = x - POINTS[i].first;
    int disY = y - POINTS[i].second;
    return abs(disX) + abs(disY) <= 2020;
}

int main() {
    int answer = 0;
    for (int i = -3000; i <= 5000; i++) {
        for (int j = -3000; j <= 5000; j++) {
            for (int k = 0; k < 4; k++) {
                if (isBlack(i, j, k)) {
                    answer++;
                    break;
                }
            }
        }
    }
    printf("%d\n", answer);
    return 0;
}

試題 C: 階乘約數

本題總分:10 分
【問題描述】
定義階乘 \(n! = 1 \times 2 \times 3 \times ··· \times n\)
請問 \(100!\)\(100\) 的階乘)有多少個約數。
【我的題解】
對於一個大於 \(1\) 的正整數 \(n\) 可以分解質因數 \(n = \prod_{i = 1} ^ {k} p_{i} ^ {a_{i}} = p_{1} ^ {a_{1}} . p_{2} ^ {a_{2}} ...... p_{k} ^ {a_{k}}\)
\(n\) 的正約數個數就是 \(f(n) = \prod_{i = 1} ^ {k} (a_{i} + 1) = (a_{1} + 1) (a_{2} + 1) ...... (a_{k} + 1)\)

#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <map>
#include <set>
#include <queue>

using namespace std;
typedef long long LL;
typedef pair<int, int> PII;

vector<int> primes;
vector<int> counter;

bool isPrime(int n) {
    for (int i = 2; i * i <= n; i++) {
        if (n % i == 0) {
            return false;
        }
    }
    return true;
}

void helper(int number) {
    int pos = 0;
    while (primes[pos] <= number) {
        while (number % primes[pos] == 0) {
            number /= primes[pos];
            counter[pos]++;
        }
        pos += 1;
    }
}

int main() {
    for (int i = 2; i <= 200; i++) {
        if (isPrime(i)) {
            primes.push_back(i);
            counter.push_back(0);
        }
    }

    for (int i = 1; i <= 100; i++) {
        helper(i);
    }

    LL answer = 1;
    for (int i = 0; i < counter.size(); i++) {
        answer *= (counter[i] + 1);
    }
    printf("%lld\n", answer);
    return 0;
}

我的答案 \(39001250856960000\)
【吐槽一下】
比賽的時候累乘 \(counter[i]\) 忘了 +1, 第一個循環只循環到了 \(100\),然后這10分就白給了。更離譜的是一樣的代碼帶回來在自己的CodeBlock上運行崩潰,但是在學校比賽的時候Dev居然能跑出一個奇怪的數字。

試題 D: 本質上升序列

本題總分:10 分
【問題描述】
小藍特別喜歡單調遞增的事物。
在一個字符串中,如果取出若干個字符,將這些字符按照在字符串中的順 序排列后是單調遞增的,則成為這個字符串中的一個單調遞增子序列。
例如,在字符串 \(lanqiao\) 中,如果取出字符 \(n\)\(q\),則 \(nq\) 組成一個單 調遞增子序列。類似的單調遞增子序列還有 \(lnq、i、ano\) 等等。
小藍發現,有些子序列雖然位置不同,但是字符序列是一樣的,例如取第 二個字符和最后一個字符可以取到 \(ao\),取最后兩個字符也可以取到 \(ao\)。小藍 認為他們並沒有本質不同。
對於一個字符串,小藍想知道,本質不同的遞增子序列有多少個?
例如,對於字符串 \(lanqiao\),本質不同的遞增子序列有 \(21\) 個。它們分別 是 \(l\)\(a\)\(n\)\(q\)\(i\)\(o\)\(ln\)\(an\)\(lq\)\(aq\)\(nq\)\(ai\)\(lo\)\(ao\)\(no\)\(io\)\(lnq\)\(anq\)\(lno\)\(ano\)\(aio\)
請問對於以下字符串(共 \(200\) 個小寫英文字母,分四行顯示):(如果你把 以下文字復制到文本文件中,請務必檢查復制的內容是否與文檔中的一致。在 試題目錄下有一個文件 inc.txt,內容與下面的文本相同)
\(tocyjkdzcieoiodfpbgcncsrjbhmugdnojjddhllnofawllbhf\) \(iadgdcdjstemphmnjihecoapdjjrprrqnhgccevdarufmliqij\) \(gihhfgdcmxvicfauachlifhafpdccfseflcdgjncadfclvfmad\) \(vrnaaahahndsikzssoywakgnfjjaihtniptwoulxbaeqkqhfwl\)
本質不同的遞增子序列有多少個?
【我的題解】
動態規划,\(dp[i]\) 表示前 \(i\) 個字母的本質不同遞增子序列個數
\(dp[i] = \sum_{j = last[s[i]] + 1} ^ {i - 1} (s[j] \lt s[i]) \times dp[j]\)
\(last[s[i]]\) 表示 \(s[i]\) 這個字母上一次出現的位置

#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <map>
#include <set>
#include <queue>

using namespace std;
typedef long long LL;
typedef pair<int, int> PII;

int last[30];
LL dp[210];
char s[210];

int main() {
    // freopen("inc.txt", "r", stdin);
    scanf("%s", s + 1);

    LL answer = 0;
    memset(last, -1, sizeof(last));

    dp[0] = 1;
    for (int i = 1; s[i]; i++) {
        int val = s[i] - 'a';
        for (int j = last[val] + 1; j < i; j++) {
            if (s[j] < s[i]) {
                dp[i] += dp[j];
            }
        }
        last[val] = i;
        answer += dp[i];
    }
    printf("%lld\n", answer);
    
    return 0;
}

我的答案 \(552\)

試題 E: 玩具蛇

本題總分:15 分
【問題描述】
小藍有一條玩具蛇,一共有 \(16\) 節,上面標着數字 \(1\)\(16\)。每一節都是一 個正方形的形狀。相鄰的兩節可以成直線或者成 \(90\) 度角。
小藍還有一個 \(4 \times 4\) 的方格盒子,用於存放玩具蛇,盒子的方格上依次標着 字母 \(A\)\(P\)\(16\) 個字母。
小藍可以折疊自己的玩具蛇放到盒子里面。他發現,有很多種方案可以將 玩具蛇放進去。
請幫小藍計算一下,總共有多少種不同的方案。如果兩個方案中,存在玩 具蛇的某一節放在了盒子的不同格子里,則認為是不同的方案。
【我的題解】
一個 \(dfs\) 就完事兒啦,作為E題倒是比想象中簡單,不過我好像寫復雜了,不需要壓縮成一維。

#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <map>
#include <set>
#include <queue>

using namespace std;
typedef long long LL;
typedef pair<int, int> PII;

const int DIRCTIONX[] = {1, -1, 0, 0};
const int DIRCTIONY[] = {0, 0, 1, -1};

int answer;
bool visited[16];
int length;

void dfs(int n) {
    if (length == 16) {
        answer++;
        return;
    }
    for (int i = 0; i < 4; i++) {
        int x = n / 4 + DIRCTIONX[i];
        int y = n % 4 + DIRCTIONY[i];
        if (x < 0 || x >= 4 || y < 0 || y >= 4) {
            continue;
        }
        int nextN = 4 * x + y;
        if (!visited[nextN]) {
            visited[nextN] = true;
            length++;
            dfs(nextN);
            visited[nextN] = false;
            length--;
        }
    }
}

int main() {
    for (int i = 0; i < 16; i++) {
        visited[i] = true;
        length++;
        dfs(i);
        visited[i] = false;
        length--;
    }
    printf("%d\n", answer);
    return 0;
}

試題 F: 皮亞諾曲線距離

時間限制: 1.0s  內存限制: 256.0MB  本題總分:15 分
【問題描述】
這里就略了,下載鏈接已給出,可以自行下載查看
【輸入格式】
輸入的第一行包含一個正整數 \(k\),皮亞諾曲線的階數。 第二行包含兩個整數 \(x1, y1\),表示第一個點的坐標。
第三行包含兩個整數 \(x2, y2\),表示第二個點的坐標。
【輸出格式】
輸出一個整數,表示給定的兩個點之間的距離。
【評測用例規模與約定】
對於 \(30\%\) 的評測用例,\(0 \le k \le 10\)
對於 \(50\%\) 的評測用例,\(0 \le k \le 20\)
對於所有評測用例,\(0 \le k \le 100, 0 \le x1,y1,x2,y2 \le 3k, x1,y1,x2,y2 \le 10 ^ {18}\)。 數據保證答案不超過 \(10 ^ {18}\)
【我的題解】
並不會做,口胡一波吧,遞歸是肯定要的,然后想到的就是以 \((0, 0)\) 為基准,求出 \((x_{1}, y_{1})\) 到基准的距離,求出 \((x_{2}, y_{2})\) 到基准的距離,然后減一下。這個遞歸感覺有點復雜呀,作為編程大題第一題有點搞心態。

試題 G: 游園安排

時間限制: 1.0s  內存限制: 256.0MB  本題總分:20 分
【問題描述】
\(L\) 星球游樂園非常有趣,吸引着各個星球的游客前來游玩。小藍是 \(L\) 星球 游樂園的管理員。
為了更好的管理游樂園,游樂園要求所有的游客提前預約,小藍能看到系 統上所有預約游客的名字。每個游客的名字由一個大寫英文字母開始,后面跟 \(0\) 個或多個小寫英文字母。游客可能重名。
小藍特別喜歡遞增的事物。今天,他決定在所有預約的游客中,選擇一部 分游客在上午游玩,其他的游客都在下午游玩,在上午游玩的游客要求按照預 約的順序排列后,名字是單調遞增的,即排在前面的名字嚴格小於排在后面的 名字。 一個名字 \(A\) 小於另一個名字 \(B\) 是指:存在一個整數 \(i\),使得 \(A\) 的前 \(i\) 個字 母與 \(B\) 的前 i 個字母相同,且 \(A\) 的第 \(i+1\) 個字母小於 \(B\) 的第 \(i+1\) 個字母。(如 果 \(A\) 不存在第 \(i+1\) 個字母且 \(B\) 存在第 \(i+1\) 個字母,也視為 \(A\) 的第 \(i+1\) 個字 母小於 \(B\) 的第 \(i+1\) 個字母) 作為小藍的助手,你要按照小藍的想法安排游客,同時你又希望上午有盡 量多的游客游玩,請告訴小藍讓哪些游客上午游玩。如果方案有多種,請輸出 上午游玩的第一個游客名字最小的方案。如果此時還有多種方案,請輸出第一 個游客名字最小的前提下第二個游客名字最小的方案。如果仍然有多種,依此 類推選擇第三個、第四個……游客名字最小的方案。
【輸入格式】
輸入包含一個字符串,按預約的順序給出所有游客的名字,相鄰的游客名 字之間沒有字符分隔。
【輸出格式】
按預約順序輸出上午游玩的游客名單,中間不加任何分隔字符。
【評測用例規模與約定】
對於 \(20\%\) 的評測數據,輸入的總長度不超過 \(20\) 個字母。
對於 \(50\%\) 的評測用例,輸入的總長度不超過 \(300\) 個字母。
對於 \(70\%\) 的評測用例,輸入的總長度不超過 \(10000\) 個字母。
對於所有評測用例,每個名字的長度不超過 \(10\) 個字母,輸入的總長度不超 過 \(1000000\) 個字母。
【我的題解】
將每個名字哈希掉,為了確保按字典序,所有名字的長度都統一成 \(10\),不足 \(10\) 的就后面用 \(0\) 填位,\(26\) 個字母加上填位用的 \(0\)\(BASE\) 就設成 \(27\) ,之后就是找最大上升子序列的經典問題了。剛好前幾題和同事回顧了一下最大上升子序列。

#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <map>
#include <set>
#include <queue>

using namespace std;
typedef long long LL;
typedef pair<int, int> PII;

const int MAXN = 1000010;
const LL INF = 0x3f3f3f3f3f3f3f3f;
const int BASE = 27;

vector<LL> hashCode;
map<LL, LL> pre;
vector<LL> dp;
char s[MAXN];
LL stack[MAXN];
int str[20];

void hashToString(LL code) {
    for (int i = 1; i <= 10; i++) {
        str[i] = code % BASE;
        code /= BASE;
    }
    putchar(str[10] + 'A' - 1);
    for (int i = 9; i >= 1; i--) {
        if (str[i] == 0) {
            break;
        }
        putchar(str[i] + 'a' - 1);
    }
}

int main() {
    int length = 0, pos = 0;
    scanf("%s", s);
    while (s[pos] != '\0') {
        LL code = s[pos++] - 'A' + 1;
        for (int j = 2; j <= 10; j++) {
            if ((s[pos] == '\0') || (s[pos] >= 'A' && s[pos] <= 'Z')) {
                code *= BASE;
                continue;
            } else {
                code = BASE* code + (s[pos++] - 'a' + 1);
            }
        }
        hashCode.push_back(code);
        dp.push_back(INF);
        length++;
    }

    dp[0] = 0;
    dp.push_back(INF);

    LL longestLength = -1;
    LL largestCode = -1;
    for (int i = 0; i < length; i++) {
        int id = lower_bound(dp.begin(), dp.end(), hashCode[i]) - dp.begin();
        pre[hashCode[i]] = dp[id - 1];
        dp[id] = hashCode[i];
        if (id > longestLength) {
            longestLength = id;
            largestCode = hashCode[i];
        }
    }

    for (int i = 1; i <= longestLength; i++) {
        stack[i] = largestCode;
        largestCode = pre[largestCode];
    }

    for (int i = longestLength; i >= 1; i--) {
        hashToString(stack[i]);
    }
    puts("");
    return 0;
}

試題 H: 答疑

時間限制: 1.0s  內存限制: 256.0MB  本題總分:20 分
【問題描述】
\(n\) 位同學同時找老師答疑。每位同學都預先估計了自己答疑的時間。 老師可以安排答疑的順序,同學們要依次進入老師辦公室答疑。
一位同學答疑的過程如下: \(1.\) 首先進入辦公室,編號為 \(i\) 的同學需要 \(s_{i}\) 毫秒的時間。 \(2.\) 然后同學問問題老師解答,編號為 \(i\) 的同學需要 \(a_{i}\) 毫秒的時間。 \(3.\) 答疑完成后,同學很高興,會在課程群里面發一條消息,需要的時間可 以忽略。 \(4.\) 最后同學收拾東西離開辦公室,需要 \(e_{i}\) 毫秒的時間。一般需要 \(10\) 秒、 \(20\) 秒或 \(30\) 秒,即 \(e_{i}\) 取值為 \(10000\)\(20000\)\(30000\)。 一位同學離開辦公室后,緊接着下一位同學就可以進入辦公室了。 答疑從 \(0\) 時刻開始。老師想合理的安排答疑的順序,使得同學們在課程群 里面發消息的時刻之和最小。
【輸入格式】
輸入第一行包含一個整數 \(n\),表示同學的數量。 接下來 \(n\) 行,描述每位同學的時間。其中第 \(i\) 行包含三個整數 \(s_{i}\), \(a_{i}\), \(e_{i}\),意 義如上所述。
【輸出格式】
輸出一個整數,表示同學們在課程群里面發消息的時刻之和最小是多少。
【評測用例規模與約定】
對於 \(30\%\) 的評測數據,\(1 \le n \le 20\)
對於 \(60\%\) 的評測用例,\(1 \le n \le 200\)
對於所有評測用例,\(1 \le n \le 1000,1 \le s_{i} \le 60000,1 \le a_{i} \le 1000000, e_{i} \in {10000,20000,30000}\),即 \(e_{i}\) 一定是 \(10000、20000、30000\) 之一。
【我的題解】
假如我在第 \(i\) 次解答了 \(j\) 同學的問題,那么 \(j\) 之后的同學都要等 \(s_{i} + a_{i} + e_{i}\) 毫秒。同時,第 \(j\) 個同學需要等待\(s_{i} + a_{i}\)。 也就是花費 \((n - i).(s_{i} + a_{i} + e_{i}) + (s_{i} + a_{i})\)。循環 \(n\) 次,每次貪心的找最小花費累加就好了。

#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <string>
#include <map>
#include <set>
#include <queue>

using namespace std;
typedef long long LL;
typedef pair<int, int> PII;

const int MAXN = 1010;
const LL INF = 0x3f3f3f3f3f3f3f3f;

LL a[MAXN], b[MAXN], c[MAXN];
bool visited[MAXN];

int main() {
    int n;
    
    scanf("%d", &n); 
    for (int i = 1; i <= n; i++) {
        scanf("%lld%lld%lld", &a[i], &b[i], &c[i]);
    }
    
    LL answer = 0;
    for (int i = 1; i <= n; i++) {
        LL minCost = INF;
        LL minPos = -1;
        for (LL j = 1; j <= n; j++) {
            if (visited[j]) {
                continue;
            }
            LL cost = a[j] + b[j];
            cost += (n - i) * (a[j] + b[j] + c[j]);
            if (cost < minCost) {
                minCost = cost;
                minPos = j;
            }
        }
        answer += minCost;
        visited[minPos] = true;
    }
    printf("%lld\n", answer);
    return 0;
}

試題 I: 出租車

時間限制: 1.0s  內存限制: 256.0MB  本題總分:25 分
【我的題解】
說實話,連題目都沒看,先挖個坑,待填,也不知道什么時候會填上。

試題 J: 質數行者

時間限制: 1.0s  內存限制: 256.0MB  本題總分:25 分
【問題描述】
小藍在玩一個叫質數行者的游戲。 游戲在一個 \(n \times m \times w\) 的立體方格圖上進行,從北到南依次標號為第 \(1\) 行到 第 \(n\) 行,從西到東依次標號為第 \(1\) 列到第 \(m\) 列,從下到上依次標號為第 \(1\) 層到 第 \(w\) 層。 小藍要控制自己的角色從第 \(1\) 行第 \(1\) 列第 \(1\) 層移動到第 \(n\) 行第 \(m\) 列第 \(w\) 層。每一步,他可以向東走質數格、向南走質數格或者向上走質數格。每走到 一個位置,小藍的角色要稍作停留。 在游戲中有兩個陷阱,分別為第 \(r_{1}\) 行第 \(c_{1}\) 列第 \(h_{1}\) 層和第 \(r_{2}\) 行第 \(c_{2}\) 列第 \(h_{2}\) 層。這兩個陷阱的位置可以跨過,但不能停留。也就是說,小藍不能控制角 色某一步正好走到陷阱上,但是某一步中間跨過了陷阱是允許的。
小藍最近比較清閑,因此他想用不同的走法來完成這個游戲。所謂兩個走 法不同,是指小藍稍作停留的位置集合不同。
請幫小藍計算一下,他總共有多少種不同的走法。
提示:請注意內存限制,如果你的程序運行時超過內存限制將不得分。
【輸入格式】
輸入第一行包含兩個整數 \(n, m, w\),表示方格圖的大小。 第二行包含 6 個整數,\(r_{1}, c_{1}, h_{1}, r_{2}, c_{2}, h_{2}\),表示陷阱的位置。
【輸出格式】
輸出一行,包含一個整數,表示走法的數量。答案可能非常大,請輸出答 案除以 \(1000000007\) 的余數。
【評測用例規模與約定】
對於 \(30\%\) 的評測用例 \(1 \le n,m,w \le 50\)
對於 \(60\%\) 的評測用例 \(1 \le n,m,w \le 300\)
對於所有評測用例,\(1 \le n,m,w \le 1000,1 \le r1,r2 \le n, 1 \le c1,c2 \le m, 1 \le h1,h2 \le w\),陷阱不在起點或終點,兩個陷阱不同。

【我的題解】
\(O(n ^ 3)\) 去動態規划,預計 \(30\%\)\(60\%\)

#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <map>
#include <set>
#include <queue>

using namespace std;
typedef long long LL;
typedef pair<int, int> PII;

const int MAXN = 310;
const int MOD = 1e9 + 7;

LL dp[MAXN][MAXN][MAXN];
int primes[] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47};

int main() {
    int n, m, w;
    int r1, c1, h1, r2, c2, h2;
    scanf("%d%d%d", &n, &m, &w);
    scanf("%d%d%d%d%d%d", &r1, &c1, &h1, &r2, &c2, &h2);
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            for (int k = 1; k <= w; k++) {
                if (i == 1 && j == 1 && k == 1) {
                    dp[i][j][k] = 1;
                }
                if (i == r1 && j == c1 && k == h1) {
                    continue;
                }
                if (i == r2 && j == c2 && k == h2) {
                    continue;
                }
                for (int p = 0; p < 15; p++) {
                    if (i - primes[p] >= 1) {
                        dp[i][j][k] = (dp[i][j][k] + dp[i - primes[p]][j][k]) % MOD;
                    }
                    if (j - primes[p] >= 1) {
                        dp[i][j][k] = (dp[i][j][k] + dp[i][j - primes[p]][k]) % MOD;
                    }
                    if (k - primes[p] >= 1) {
                        dp[i][j][k] = (dp[i][j][k] + dp[i][j][k - primes[p]]) % MOD;
                    }
                }
            }
        }
    }
    printf("%lld\n", dp[n][m][w]);
    return 0;
}

總結

國二退役啦


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM