【解】第十一届蓝桥杯c++b组填空题题解

前言

本次填空题偏简单,编程大题有难(keng)度(die),4个钟,1个钟ak填空,3个钟写编程大题时间还是很紧张,算法以暴力为主,并没有时间想最优解。

试题 A: 美丽的 2

【解】第十一届蓝桥杯c++b组填空题题解_第1张图片
思路:签到题,简单模拟一下即可

#include

using namespace std;
/*
	答案:563
*/
bool is(int year) {
    while (year) {
        if (year % 10 == 2) return true;
        year /= 10;
    }    
    return false;
}

int main() {
    int res = 0;
    for (int i = 1; i <= 2020; i++) if (is(i)) res++;
    cout << res << endl;
    return 0;
}

试题 B: 扩散

【解】第十一届蓝桥杯c++b组填空题题解_第2张图片
读完题目,易知这是道bfs的模板题,从4个点开始,不断向四个方向扩散,唯一比较难办的是如何解决坐标为负数数组会越界的问题,比赛时我想的最直接的方法是将给定一个偏移量base,将坐标上的点进行整体的移动,但是另一种思路更加的简单,用哈希表的思想,将每个坐标映射成一个数且这个数是唯一的,然后可以用哈希表来维护出现过的坐标 ,虽然程序跑起来会比较长

#include

using namespace std;
#define fi first
#define se second
typedef long long LL;
typedef pair<int, int> PII;
int dx[4] = {0, -1, 0, 1};
int dy[4] = {1, 0, -1, 0};
/*
	答案:20312088
*/
// 映射函数f,系数可以随便取,原则是让每个坐标唯一对应一个数 
LL f(LL u, LL v) {
    return u * 2333333 + v;
}

LL bfs() {
    queue<PII> q;
    // unordered_set是c++11的东西,可以换成set,不过unordered_set的查找效率会比set高 
    unordered_set<LL> se;
    // 先把四个点插入进来 
    q.push({0, 0});
    q.push({2020, 11});
    q.push({11, 14});
    q.push({2000, 2000});
    se.insert(f(0, 0));
    se.insert(f(2020, 11));
    se.insert(f(11, 14));
    se.insert(f(2000, 2000));
    int cnt = 0;
    LL res = 4;
    while (!q.empty()) {
        int sz = q.size();
        // bfs第i层对应经过了i分钟 
        for (int i = 1; i <= sz; i++) {
            auto t = q.front();
            q.pop();
            for (int j = 0; j < 4; j++) {
                int u = dx[j] + t.fi, v = dy[j] + t.se;
                // 判重 
                if (se.count(f(u, v))) continue;
                se.insert(f(u, v));
                q.push({u, v});
                res++;
            }
        }
        if (++cnt >= 2020) return res; 
    }
}

int main() {
    cout << bfs() << endl;
    return 0;
}

试题 C: 阶乘约数

【解】第十一届蓝桥杯c++b组填空题题解_第3张图片
数论题,首先由算术基本定理我们可以给出一个数被分解后质数及其个数
N = P 1 a 1 P 2 a 2 P 3 a 3 . . . P n a n N = P_{1}^{a_1}P_{2}^{a_2}P_{3}^{a_3}...P_{n}^{a_n} N=P1a1P2a2P3a3...Pnan
其中 P i P_i Pi为质数, a i a_i ai为正整数
我们可以推出一条性质,N的约数的个数为 ( a 1 + 1 ) ( a 2 + 1 ) ( a 3 + 1 ) . . . ( a n + 1 ) ({a_1} + 1)({a_2} + 1)({a_3} + 1)...({a_n} + 1) (a1+1)(a2+1)(a3+1)...(an+1) 这里可以用组合数学 (背包思想) 的办法来解释,约数一定是由不同的质因数组成的,那么每种质数我选择若干个,假设质数 P i P_i Pi可以选0,1,2, a i a_i ai个,一共有( a i a_i ai + 1)种方法,根据乘法原理,把每种质数选择的个数相乘起来即可

#include

using namespace std;
typedef long long LL;
LL cnt[110];
/*
    答案:39001250856960000
*/
// 试除法求质因数
void get(int x) {
    for (int i = 2; i <= x / i; i++) {
        if (x % i == 0) {
            while (x > 1 && x % i == 0) cnt[i]++, x /= i;
        }
    }
    if (x > 1) cnt[x]++;
}

int main() {
    for (int i = 1; i <= 100; i++) get(i);
    LL res = 1;
    for (int i = 2; i <= 100; i++) res *= (cnt[i] + 1);
    
    cout << res << endl;
    
    return 0;
}

试题 D: 本质上升序列

【解】第十一届蓝桥杯c++b组填空题题解_第4张图片
经典dp问题,这题和递增子序列求方案数相比,多了一个条件,相同的递增子序列只算1次,我们可以先从递增子序列求方案数(包含重复的)开始思考, f [ i ] f[i] f[i]表示以 a [ i ] a[i] a[i]结尾的字符的递增上升子序列方案数,很容易写出状态转移方程
f [ i ] = f [ i ] + f [ j ] , a [ i ] > a [ j ] , j < i < = n f[i] = f[i] + f[j], a[i] > a[j],j < i <= n f[i]=f[i]+f[j],a[i]>a[j]j<i<=n
本质不同递增子序列说白了就是同一个递增子序列只算一次,那么上式最后得出的结果一定是有重复计数的,所以我们要把这一部分剪掉,我们可以重新定义一下 f [ i ] f[i] f[i]表示以i结尾的不同递增子序列的个数,当 i < j , a [ i ] = = a [ j ] i < j, a[i] == a[j] i<j,a[i]==a[j]显然以a[i]结尾的递增子序列方案数都包含在了f[i]当中了,那么f[j]就不必再去计算i前面的递增子序列方案数了,所以碰到 i < j , a [ i ] = = a [ j ] i < j, a[i] == a[j] i<j,a[i]==a[j],break就ok了

#include
/*
    答案:3616159
    输入:
    tocyjkdzcieoiodfpbgcncsrjbhmugdnojjddhllnofawllbhf
    iadgdcdjstemphmnjihecoapdjjrprrqnhgccevdarufmliqij
    gihhfgdcmxvicfauachlifhafpdccfseflcdgjncadfclvfmad
    vrnaaahahndsikzssoywakgnfjjaihtniptwoulxbaeqkqhfwl
*/

using namespace std;
typedef long long LL;
const int N = 1010;
string str;
// 记录字符i是否出现过
int st[N], f[N];
int main() {
    cin >> str;
    int n = str.size();
    for (int i = 0; i < n; i++) {
        if (!st[str[i] - 'a']) f[i] = 1;
        st[str[i] - 'a'] = true;
        /* 细节:这里需要逆序统计,因为正序统计f[i]加入的方案数时,
          万一碰到a[i] == a[j]
          显然f[i]正序统计的方案数会和f[j]有重复的方案
        */
        for (int j = i - 1; j >= 0; j--) {
            if (str[i] > str[j]) f[i] += f[j];
            else if (str[i] == str[j])  break;
        }
    }
    // 最后别忘记统计所以以str[i]结尾的所有方案数
    LL res = 0;
    for (int i = 0; i < n; i++) res += f[i];
    
    cout << res << endl;
    return 0;
}

试题 E: 玩具蛇

【解】第十一届蓝桥杯c++b组填空题题解_第5张图片
这个问题很抽象但是考场一下想到用dfs其实这题不用dfs也能做,将1-16放到格子里,时间复杂度度为 O ( 16 ! ) O(16!) O(16!),全排列问题然后用判断一下该方案是否合法, 分析问题可以得到的合法的方案即:从1出发往4个方向走,走16次,每个格子有且仅被走过一次。这里还是用dfs解吧

#include

using namespace std;
int dx[4] = {0, 1, 0, -1};
int dy[4] = {1, 0, -1, 0};
int st[5][5], res = 0;
/*
    答案:552
*/
void dfs(int cnt, int u, int v) {
    if (cnt >= 16) {
        res++;
        return;
    }
    
    for (int i = 0; i < 4; i++) {
        int x = dx[i] + u, y = dy[i] + v;
        if (x <= 0 || y <= 0 || x > 4 || y > 4 || st[x][y]) continue;
        st[x][y] = 1;
        dfs(cnt + 1, x, y);
        // 回溯
        st[x][y] = 0;
    }
}

int main() {
    // 枚举起点
    for (int i = 1; i <= 4; i++)
        for (int j = 1; j <= 4; j++) {
            st[i][j] = 1;
            dfs(1, i, j);
            st[i][j] = 0;
        }
    cout << res << endl;
    return 0;
}

小结

本次填空题注重基础算法的应用,如简单模拟、bfs、简单数论、dfs、dp考点比较全面,读题细心一点就不会有太大问题

你可能感兴趣的:(数据结构,算法)