2017 蓝桥杯 A组 题目 C++

344 阅读5分钟

A.迷宫

题目

X星球的一处迷宫游乐场建在某个小山坡上。它是由10x10相互连通的小房间组成的。 房间的地板上写着一个很大的字母。我们假设玩家是面朝上坡的方向站立,则: L表示走到左边的房间,R表示走到右边的房间,U表示走到上坡方向的房间,D表示走到下坡方向的房间。 X星球的居民有点懒,不愿意费力思考。他们更喜欢玩运气类的游戏。这个游戏也是如此! 开始的时候,直升机把100名玩家放入一个个小房间内。玩家一定要按照地上的字母移动。 迷宫地图如下:

UDDLUULRUL
UURLLLRRRU
RRUURLDLRD
RUDDDDUUUU
URUDLLRRUU
DURLRLDLRL
ULLURLLRDU
RDLULLRDDD
UUDDUDUDLL
ULRDLUURRR

请你计算一下,最后,有多少玩家会走出迷宫? 而不是在里边兜圈子。

img

思路

只需要一个接一个遍历,通过dfs,简单搜索即可得到答案

代码

#include <bits/stdc++.h>
using namespace std;
#define mm(a) memset(a,0,sizeof(a));

int Map[10][10]; // 记录该位置是否被访问
string data[10]; // 存放数据
 
bool dfs(int row, int col) {
    if (row < 0 || row > 9 || col < 0 || col > 9) return true;
    if (Map[row][col] == 1) return false;
    Map[row][col] = 1;
    if (data[row][col] == 'U') return dfs(row - 1, col);
    else if(data[row][col] == 'D') return dfs(row + 1, col);
    else if (data[row][col] == 'L') return dfs(row, col - 1);
    else return dfs(row, col + 1);
}

int main()
{
    // 读入数据
    data[0] = "UDDLUULRUL";
    data[1] = "UURLLLRRRU";
    data[2] = "RRUURLDLRD";
    data[3] = "RUDDDDUUUU";
    data[4] = "URUDLLRRUU";
    data[5] = "DURLRLDLRL";
    data[6] = "ULLURLLRDU";
    data[7] = "RDLULLRDDD";
    data[8] = "UUDDUDUDLL";
    data[9] = "ULRDLUURRR";
    int res = 0;
    for (int row = 0; row < 10; row++) {
        for (int col = 0; col < 10; col++) {
            mm(Map);
            if (dfs(row, col)) res++;
        }
    }
    cout << res << endl;
    return 0;
}

B.跳蚱蜢

题目

如图所示: 有9只盘子,排成1个圆圈。其中8只盘子内装着8只蚱蜢,有一个是空盘。

img

我们把这些蚱蜢顺时针编号为 1~8。每只蚱蜢都可以跳到相邻的空盘中,也可以再用点力,越过一个相邻的蚱蜢跳到空盘中。 请你计算一下,如果要使得蚱蜢们的队形改为按照逆时针排列,并且保持空盘的位置不变(也就是1-8换位,2-7换位,...),至少要经过多少次跳跃?

思路

直接让蚱蜢跳到空盘有点麻烦。如果看成空盘跳到蚱蜢的位置就简单多了,只有一个空盘在跳。

题目给的是一个圆圈,不好处理,此时祭出一个建模大法:“化圆为线”! 把空盘看成0,

那么有9个数字{0,1,2,3,4,5,6,7,8},一个圆圈上的9个数字,拉直成了一条线上的9个数字。

八数码是经典的BFS问题。

八数码有9个数字{0,1,2,3,4,5,6,7,8},它有9!=362880种排列。也不多,

本题的初始状态是“012345678”,终止状态是“087654321”。

把9个数字的排列定义为一种状态,即字符串s,例如初始状态“012345678”是一个串。对

应交换之后产生的s,我们可以使用map判重,将该字符串以及它首次出现的时间作为一个单

位推入队列中,由于BFS的性质我们能看出,首次找到结果状态的时间t即是最小的答案。

代码

#include <bits/stdc++.h>
using namespace std;
#define mm(a) memset(a,0,sizeof(a));

struct node{
    node() {}
    node(string ss, int tt) {
        s = ss;
        step = tt;
    }
    string s;
    int step;
};

map<string, bool> mp; // 表示 某一个字符串是否被遍历
queue<node> q;        // 队列用与bfs
void bfs() {
    while (!q.empty()) {
        node cur = q.front(); 
        q.pop();
        string s = cur.s;
        int step = cur.step;
        if (s == "087654321") {
            cout << step << endl;
            break;
        }
        // 找到空位
        int pos = 0;
        while (s[pos] != '0') pos++;
        // 交换位置 重新入队
        for (int sp = pos - 2; sp <= pos + 2; sp++) {
            int k = (sp + 9) % 9;                      // 为了形成环形 需要 加9之后模9
            if (k == pos) continue;
            char temp;
            temp = s[pos]; s[pos] = s[k]; s[k] = temp; // 交换得到新的字符串
            if (!mp[s]) {
                mp[s] = true;
                q.push(node(s, step + 1));
            }
            temp = s[pos]; s[pos] = s[k]; s[k] = temp; // 重新交换回来,进行判断下一个
        }

    }
}

int main() {
    string s = "012345678";
    q.push(node(s, 0));
    mp[s] = true;
    bfs();
    return 0;
}

C.模仿状态

题目

二阶魔方就是只有2层的魔方,只由8个小块组成。如图所示。 img 小明很淘气,他只喜欢3种颜色,所有把家里的二阶魔方重新涂了颜色,如下: 前面:橙色、右面:绿色、上面:黄色、左面:绿色、下面:橙色、后面:黄色 请你计算一下,这样的魔方被打乱后,一共有多少种不同的状态。 如果两个状态经过魔方的整体旋转后,各个面的颜色都一致,则认为是同一状态。

思路

不会blog.csdn.net/weixin_4391…

img

代码

#include <bits/stdc++.h>
using namespace std;
#define mm(a) memset(a,0,sizeof(a));

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

D.方格分割

题目

6x6的方格,沿着格子的边线剪开成两部分。要求这两部分的形状完全相同。如图就是可行的分割法。 imgimgimg 试计算:包括这3种分法在内,一共有多少种不同的分割方法。注意:旋转对称的属于同一种分割法。

思路

回溯算法(DFS):由题意,这一条切割线必定经过图的中心点,那么我们一旦确定了半条到达边界的分割线,就能根据这半条对称画出另外半条。而由于结果中心对称性,搜索出来的个数应该除以4得出最终结论。

  • 中心点是(3,3),从(3,3)出发,向右、向左、向上、向下,四个方向DFS即可。

可以直接套用回溯的模版就可以了

代码

#include <bits/stdc++.h>
using namespace std;
#define mm(a) memset(a,0,sizeof(a));

int Map[10][10];
int res = 0;
int X[] = {0, -1, 1, 0, 0};
int Y[] = {0, 0, 0, -1, 1};
void dfs(int x, int y) {
    if (x == 0 || y == 0 || x == 6 || y == 6) {
        res++; 
        return ;
    }
    for (int i = 1; i <= 4; i++) {
        x += X[i], y += Y[i];
        if (!Map[x][y]) {
            Map[x][y] = 1;
            Map[6 - x][6 - y] = 1;
            dfs(x, y);
            Map[6 - x][6 - y] = 0;
            Map[x][y] = 0;
        }
        x -= X[i], y -= Y[i];
    }
}


int main() {
    mm(Map);
    Map[3][3] = 1;
    dfs(3, 3);
    cout << res / 4 << endl;
    return 0;
}

E.字母组串

题目

由 A,B,C 这3个字母就可以组成许多串。 比如:"A","AB","ABC","ABA","AACBB" ....

现在,小明正在思考一个问题: 如果每个字母的个数有限定,能组成多少个已知长度的串呢?

他请好朋友来帮忙,很快得到了代码, 解决方案超级简单,然而最重要的部分却语焉不详。

请仔细分析源码,填写划线部分缺少的内容。

#include <stdio.h>

// a个A,b个B,c个C 字母,能组成多少个不同的长度为n的串。
int f(int a, int b, int c, int n)
{
	if(a<0 || b<0 || c<0) return 0;
	if(n==0) return 1; 
	
	return ______________________________________ ;  // 填空
}

int main()
{
	printf("%d\n", f(1,1,1,2));
	printf("%d\n", f(1,2,3,3));
	return 0;
}

思路

动态规划(DP)

很明显,有一个状态关系式,f(n) = f(n - 1) {A, B, C} 第n个字符串就是第n - 1个字符串后面加A或B或C,有点类似于爬楼梯那个递归方程。

代码

return f(a - 1, b, c, n - 1) + f(a, b - 1, c, n - 1) + f(a, b, c - 1, n - 1);

F.最大公共子串

题目

最大公共子串长度问题就是: 求两个串的所有子串中能够匹配上的最大长度是多少。

比如:“abcdkkk” 和 “baabcdadabc”, 可以找到的最长的公共子串是"abcd",所以最大公共子串长度为4。

下面的程序是采用矩阵法进行求解的,这对串的规模不大的情况还是比较有效的解法。

#include <stdio.h>
#include <string.h>

#define N 256
int f(const char* s1, const char* s2)
{
	int a[N][N];
	int len1 = strlen(s1);
	int len2 = strlen(s2);
	int i,j;
	
	memset(a,0,sizeof(int)*N*N);
	int max = 0;
	for(i=1; i<=len1; i++){
		for(j=1; j<=len2; j++){
			if(s1[i-1]==s2[j-1]) {
				a[i][j] = __________________________;  //填空
				if(a[i][j] > max) max = a[i][j];
			}
		}
	}
	
	return max;
}

int main()
{
	printf("%d\n", f("abcdkkk", "baabcdadabc"));
	return 0;
}

思路

这道题也是经典的动态规划题目,只需要找到状态转移方程式,就可以得到答案。

代码

a[i][j] = a[i - 1][j - 1] + 1;

G.正则问题

题目

考虑一种简单的正则表达式:只由 x ( ) | 组成的正则表达式。 小明想求出这个正则表达式能接受的最长字符串的长度。 例如 ((xx|xxx)x|(x|xx))xx 能接受的最长字符串是: xxxxxx,长度是6

输入

输入一个由x()|组成的正则表达式。输入长度不超过100,保证合法。  
如:
((xx|xxx)x|(x|xx))xx  

输出

输出这个正则表达式能接受的最长字符串的长度。
6

思路

IMG_0202

之前做过类似的题目,读题没仔细,看到题目就直接做了,然后提交,38分,然后改代码,提交63分,再改代码,75分。

image-20210412170527400

然后就放弃了,去搜了一下别人的思路,看了下也是用栈,但是看了下别人的分析,我发现我忽略了一点很关键的东西

以这个为例子:(()),我直接默认为了(())内层两个匹配,但是可能(()**)**这样匹配。

代码

如果按照我的思路那样匹配的话,直接用栈,就可以完成正则匹配

#include <bits/stdc++.h>
using namespace std;
#define mm(a) memset(a,0,sizeof(a));
#define max(x,y) (x)>(y)?(x):(y)
#define min(x,y) (x)<(y)?(x):(y)
int main() {
    stack<char> stk;
    string s;
    cin >> s;
    for (int i = 0; i < s.length(); i++) {
        if (s[i] == ')') {
            string cur = ""; // 定义一个字符串去 接收栈里的表达式
            while (stk.top() != '(') {
                cur += stk.top();
                stk.pop();
            }
            stk.pop(); // pop 出‘(’
            int j;
            int MAX = 0, cnt = 0;
            for (j = 0; j < cur.length(); j++) {
                if (cur[j] == '|') {
                    MAX = max(MAX, cnt);
                    cnt = 0;
                } else {
                    cnt++;
                }
            }
            MAX = max(MAX, cnt);
            while (MAX--) stk.push('x');
        } else {
            stk.push(s[i]);
        }
    }

    string cur = "";
    while (!stk.empty()) {
        cur += stk.top();
        stk.pop();
    }
    int j;
    int MAX = 0, cnt = 0;
    for (j = 0; j < cur.length(); j++) {
        if (cur[j] == '|') {
            MAX = max(MAX, cnt);
            cnt = 0;
        } else {
            cnt++;
        }
    }
    MAX = max(MAX, cnt);
    cout << MAX << endl;
    return 0;
}

改进之后

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

string s = "";  // target string
int pos = 0;    // current postion

int dfs() {
    int tmp = 0; // temporary variable to store the character of x
    int ret = 0; // return result; 
    int len = s.size();
    while (pos < len) {
        if (s[pos] == '(') {
            // (   continue recursion, equivalent to stacking
            pos++;
            tmp += dfs();
        } else if (s[pos] == ')') {
            // )  return recursion, equivalent to popping
            pos++;
            break;
        } else if (s[pos] == '|') {
            // check or
            pos++;
            ret = max(ret, tmp);
            tmp = 0;
        } else {
            // calculate and count the character of x
            pos++;
            tmp++;
        }
    }
    return max(ans, tmp);
}


int main() {
    cin >> s;
    cout << dfs() << endl;
    return 0;
}

H.包子凑数

题目

题目描述

小明几乎每天早晨都会在一家包子铺吃早餐。这家包子铺有N种蒸笼,其中第i种蒸笼恰好能放Ai个包子 每种蒸笼都有非常多笼,可以认为是无限笼。 每当有顾客想买X个包子,卖包子的大叔就会选出若干笼包子来,使得这若干笼中恰好一共有X个包子。 比如一共有3种蒸笼,分别能放3、4和5个包子。当顾客想买11个包子时,大叔就会选2笼3个的再加1笼5个的(也可能选出1笼3个的再加2笼4个的)。 当然有时包子大叔无论如何也凑不出顾客想买的数量。 比如一共有3种蒸笼,分别能放4、5和6个包子。而顾客想买7个包子时,大叔就凑不出来了。 小明想知道一共有多少种数目是包子大叔凑不出来的。

输入

第一行包含一个整数N。(1 <= N <= 100) 以下N行每行包含一个整数Ai。(1 <= Ai <= 100)

输出

输出一行包含一个整数代表答案。如果凑不出的数目有无限多个,输出INF。

样例输入

2
4
5

样例输出

6

提示

对于样例,凑不出的数目包括:1, 2, 3, 6, 7, 11。

代码

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

const int MAXN = 13000;
int a[101];
int dp[MAXN] = {0};
int gcd(int a, int b) {
    return b > 0 ? gcd(b, a % b) : a;
}

int main() {
    int n;
    cin >> n;
    for (int i = 1; i <= n; i++) 
        cin >> a[i];
    int g = a[1];
    for (int i = 2; i <= n; i++) 
        g = gcd(g, a[i]);
    if (g != 1) {
        cout << "INF" << endl;
        return 0;
    } 
    for (int i = 1; i <= n; i++) {
        dp[a[i]] = 1;  // 遍历第i个数据 表是那个值是计算过的
        for (int j = 0; j + a[i] < 10000; j++) {
            if (dp[j]) {
                dp[j + a[i]] = 1; // 原本那个可以筹出来的数 + 当前这个数字 也一定是一个能够筹出来的数字
            }
        }
    }
    int res = 0;
    for (int i = 1; i < 10000; i++) {
        if (dp[i] == 0) res++;
    }
    cout << res << endl;
    return 0;
}
 

I.分巧克力

题目

题目描述

儿童节那天有K位小朋友到小明家做客。小明拿出了珍藏的巧克力招待小朋友们。 小明一共有N块巧克力,其中第i块是Hi x Wi的方格组成的长方形。 为了公平起见,小明需要从这 N 块巧克力中切出K块巧克力分给小朋友们。切出的巧克力需要满足:

  1. 形状是正方形,边长是整数
  2. 大小相同
    • 例如一块6x5的巧克力可以切出6块2x2的巧克力或者2块3x3的巧克力。
    • 当然小朋友们都希望得到的巧克力尽可能大,你能帮小Hi计算出最大的边长是多少么?

输入

第一行包含两个整数N和K。(1 <= N, K <= 100000) 以下N行每行包含两个整数Hi和Wi。(1 <= Hi, Wi <= 100000) 输入保证每位小朋友至少能获得一块1x1的巧克力。

输出

输出切出的正方形巧克力最大可能的边长。

样例输入

2 10
6 5
5 6

样例输出

2

思路

暴力枚举: 但是数据规模是10万,n个长方形,长方形的最大边长D,复杂度是O ( n × D ) ,10^10会TLE

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

const int MAXN = 100010;
int h[MAXN], w[MAXN];
int n, k;

bool check(int d) {
    int num = 0;
    for (int i = 0; i < n; i++) 
        num += (h[i] / d) * (w[i] / d);
    if (num >= k) return true;
    else return false;
}

int main() {
    cin >> n >> k;
    for (int i = 0; i < n; i++) 
        cin >> h[i] >> w[i];
    int d = 1;
    while (1) {
        if (check(d))
            d++;
        else 
            break;
    }
    cout << d - 1 << endl;
    return 0;

}

二分优化:既然找最大的那条边D太高,有二分查找可以降为logD

代码

OJ上AC c++代码

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

const int MAXN = 100010;
int h[MAXN], w[MAXN];
int n, k;

bool check(int d) {
    int num = 0;
    for (int i = 0; i < n; i++) 
        num += (h[i] / d) * (w[i] / d);
    if (num >= k) return true;
    else return false;
}

int main() {
    cin >> n >> k;
    for (int i = 0; i < n; i++) 
        cin >> h[i] >> w[i];
    int l = 1, r = MAXN;
    while (l <= r) {
        int mid = (l + r) / 2;
        if (check(mid))
            l = mid + 1;
        else 
            r = mid - 1;
    }
    cout << l - 1 << endl;
    return 0;

}

J.付账问题

题目

题目描述

几个人一起出去吃饭是常有的事。但在结帐的时候,常常会出现一些争执。 现在有 n 个人出去吃饭,他们总共消费了 S 元。其中第 i 个人带了 ai 元。 幸运的是,所有人带的钱的总数是足够付账的。但现在问题来了:每个人分别要出多少钱呢? 为了公平起见,我们希望在总付钱量恰好为 S 的前提下,最后每个人付的钱的标准差最小。 这里我们约定,每个人支付的钱数可以是任意非负实数,即可以不是1分钱的整数倍。你需要输出最小的标准差是多少。 标准差的介绍:标准差是多个数与它们平均数差值的平方平均数,一般用于刻画这些数之间的“偏差有多大”。 形式化地说,设第 i 个人付的钱为 bi 元,那么标准差为 : img

输入

第一行包含两个整数 n、S; 第二行包含 n 个非负整数 a1, ..., an。 n ≤ 5 × 10^5, 0 ≤ ai ≤ 10^9。

输出

输出最小的标准差,四舍五入保留 4 位小数。 保证正确答案在加上或减去 10^−9 后不会导致四舍五入的结果发生变化。

样例输入

10 30
2 1 4 7 4 8 3 6 4 7

样例输出

0.7928

思路  

  • 对a i 从小到大排序;
  • 前一部分人的钱不够,那么就出他们所有的钱;
  • 从总付钱数中扣除前一部分人出的钱,得剩余钱数为S ’,以及后一部分人的出钱平均数avg。
  • 后一部分人的钱多,他们多出一些。怎么出?这部分人也分两类:
    • 比较有钱的,但是他的钱也不够avg ,那么他的钱还是要全出;
    • 非常有钱的,不管怎么摊他都有富余。

代码

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

const int MAXN = 5e5;
typedef long long ll;

ll a[MAXN];
int main() {
    int n;
    ll s;
    cin >> n >> s;
    for (int i = 1; i <= n; i++) cin >> a[i];

    sort(a + 1, a + n + 1);
    double avg = 1.0 * s / n;
    double sum = 0;
    for (int i = 1; i <= n; i++) {
        if (a[i] * (n + 1 - i) < s) {           // 需要把钱全拿出的人:(1)钱不够平均数的,(2)钱够平均数,但也不是很多的
            sum += (a[i] - avg) * (a[i] - avg);
            s -= a[i];                          // 更新剩余钱
        } else {
            // 不用把钱全拿出的人:非常有钱,不管怎么平均都够
            double cur_avg = 1.0 * s / (n + 1 - i); // 更新平均出钱
            sum += pow((cur_avg - avg), 2) * (n + 1 - i);
            break;
        }
    }
    printf("%.4f\n", sqrt(sum / n));
    return 0;
}