蓝桥杯2016年省赛b组题解

第1题 煤球数目

题目:
有一堆煤球,堆成三角棱锥形。具体:
第一层放1个,
第二层3个(排列成三角形),
第三层6个(排列成三角形),
第四层10个(排列成三角形),
….
如果一共有100层,共有多少个煤球?
请填表示煤球总数目的数字。
注意:你提交的应该是一个整数,不要填写任何多余的内容或说明性文字。

分析:
根据下面的图,可以看出,煤球的堆叠是第11个,第21+2个,第31+2+3个……设煤球层数为n,那么第n层的每球数符合等差数列前n项和公式,如下图所示,则答案很明确了:

第一题 煤球数目

代码:

#include <iostream>
using namespace std;

int main()
{
    long long sum = 0;
    for (int i = 1; i <= 100; i++)
        sum += (i * (1 + i)) / 2;
    cout << sum;
    return 0;
}

答案:171700
标签:简单计算、数学归纳

第2题 生日蜡烛

题目:
某君从某年开始每年都举办一次生日party,并且每次都要吹熄与年龄相同根数的蜡烛。
现在算起来,他一共吹熄了236根蜡烛。
请问,他从多少岁开始过生日party的?
请填写他开始过生日party的年龄数。
注意:你提交的应该是一个整数,不要填写任何多余的内容或说明性文字。

分析:
从某一年龄开始过生日的年龄为a,现在的年龄为b,那么直到吹熄236根,即由ab的等差数列之和为236,可以通过遍历搜索的方式来求得答案中的a
代码:

#include <iostream>
using namespace std;

int main()
{
    for (int a = 1; a <= 100; a++)
    {
        for (int b = 1; b <= 100; b++)
        {
            if ((b - a + 1)*(a + b) / 2 == 236)
                cout << a;
        }
    }
    return 0;
}

答案:26
标签:遍历枚举

第3题 凑算式

题目:


这个算式中AI代表19的数字,不同的字母代表不同的数字。
比如:
6+8/3+952/714 就是一种解法,
5+3/1+972/486 是另一种解法。
这个算式一共有多少种解法?
注意:你提交应该是个整数,不要填写任何多余的内容或说明性文字。

分析:
这个题可以有三种解法,一种是暴力枚举一种是DFS,一种是直接利用next_permutation()函数,三种方法的目的相同,都是求取1~9的全排列,并且9个数字不能重复,下面给出后两种方法的代码。
DFS代码:

#include <iostream>
using namespace std;
int ans = 0; //存放答案
double a[10]; //存放十个数字的全排列
int mark[10] = {0}; //记录10个数字是否被选中,防止重复

void dfs(int step)
{
    if (step == 10)
    {
        if (a[1] + a[2] / a[3] + (a[4] * 100 + a[5] * 10 + a[6]) / (a[7] * 100 + a[8] * 10 + a[9]) == 10)
            ans++; //找到一种答案
        return;
    }
    
    for (int i = 1; i <= 9; i++) //遍历9个数,全排列的DFS套路
    {
        if (mark[i] == 0) //如果未被选中
        {
            a[step] = i; //加入序列
            mark[i] = 1; //标记已经看过
            dfs(step + 1); //继续探索
            mark[i] = 0; //再恢复
        }
        //return;
    }
}

int main()
{
    dfs(1);
    cout << ans <<endl;
    return 0;
}

使用next_permutation()函数代码:

#include <iostream>
#include <algorithm>
using namespace std;
int main(int argc, char *argv[]) {
    int a[10] = { 0,1,2,3,4,5,6,7,8,9 };
    int sum = 0;
    while (next_permutation(a + 1, a + 10))
    {
        int lala = a[1] + a[2] / a[3] + (a[4] * 100 + a[5] * 10 + a[6]) / (a[7] * 100 + a[8] * 10 + a[9]);
        if (lala == 10)
            sum++;
    }
    cout << sum;
    return 0;
}

注意:必须变成浮点型的数才可以比较,否则结果不正确

答案:29
标签:暴力枚举、全排列

第4题 快速排序

题目:
排序在各种场合经常被用到。
快速排序是十分常用的高效率的算法。
其思想是:先选一个“标尺”,
用它把整个队列过一遍筛子,
以保证:其左边的元素都不大于它,其右边的元素都不小于它。
这样,排序问题就被分割为两个子区间。
再分别对子区间排序就可以了。
下面的代码是一种实现,请分析并填写划线部分缺少的代码。

代码:

#include <stdio.h>

void swap(int a[], int i, int j) //简单的首尾相接交换函数
{
    int t = a[i];
    a[i] = a[j];
    a[j] = t;
}

int partition(int a[], int p, int r) //分割函数
{
    int i = p;
    int j = r + 1;
    int x = a[p]; //取第一个作为pivot
    while (1) {
        while (i < r && a[++i] < x); //找到左边第一个大于pivot的
        while (a[--j] > x);// 找到右边第一个小于pivot的
        if (i >= j) break; //当j跑到i左边就结束
        swap(a, i, j); //交换两者位置
    }
    //______________________;
    swap(a, p, j);
    return j;
}

void quicksort(int a[], int p, int r)
{
    if (p < r) {
        int q = partition(a, p, r); //分割函数,把大的放到右边,小的放在左边
        quicksort(a, p, q - 1); //对左边递归排序
        quicksort(a, q + 1, r); //对右边递归排序
    }
}

int main()
{
    int i;
    int a[] = { 5,13,6,24,2,8,19,27,6,12,1,17 }; //定义一个数组,待排序
    int N = 12; //元素数目

    quicksort(a, 0, N - 1); //快速排序函数

    for (i = 0; i < N; i++) printf("%d ", a[i]); //输出结果
    printf("\n");

    return 0;
}

分析:这类题目直接将代码复制粘贴到编译器中运行并作注释,一点点分析。分析过程在上面的代码中,其他填空题目都如此,分析过程略。

答案:swap(a, p, j)
标签:快速排序

第5题 抽签

题目:
X星球要派出一个5人组成的观察团前往W星。
其中:
A国最多可以派出4人。
B国最多可以派出2人。
C国最多可以派出2人。
….
那么最终派往W星的观察团会有多少种国别的不同组合呢?
下面的程序解决了这个问题。
数组a[] 中既是每个国家可以派出的最多的名额。
程序执行结果为:
DEFFF
CEFFF
CDFFF
CDEFF
CCFFF
CCEFF
CCDFF
CCDEF
BEFFF
BDFFF
BDEFF
BCFFF
BCEFF
BCDFF
BCDEF
….
(以下省略,总共101行)

代码:

#include <stdio.h>
#include <iostream>
#define N 6
#define M 5
#define BUF 1024
using namespace std;
int cnt = 0;

void f(int a[], int k, int m, char b[])
{
    int i, j;

    if (k == N) {
        b[M] = 0; //字符串结尾标志
        if (m == 0) { printf("%s\n", b);cnt++;};
        return;
    }

    for (i = 0; i <= a[k]; i++) { //循环第K个国家人数
        for (j = 0; j < i; j++) b[M - m + j] = k + 'A'; //填充字符串b,几个人几个相同的国家标记k+'A'
        //______________________;  //填空位置
        f(a, k + 1, m - i ,b); //继续探索下一个国家
    }
}

int main()
{
    int  a[N] = { 4,2,2,1,1,3 };
    char b[BUF]; //存放字符串
    f(a, 0, M, b);
    cout << cnt;
    return 0;
}

答案:f(a, k + 1, m - i ,b)或者f(a,k+1,m-j,b)
标签:DFS、递归

第6题 方格填数

题目:
如图,如下的10个格子,填入0~9的数字。要求:连续的两个数字不能相邻。
(左右、上下、对角都算相邻)一共有多少种可能的填数方案?
请填写表示方案数目的整数。

第6题 方格填数

分析: 这题是一道结果填空,所以又可以用暴力递归来求解,建立一个数组,用next_permutation()函数来进行全排列,判断一下即可。注意对角线也不能一样。(这里有个想法,因为判断条件很多,可以把条件单独列出来,赋给bool变量,判断的时候就方便很多)
代码:

#include <iostream>
#include <algorithm>
using namespace std;

bool judge(int arr[])
{
    bool j1, j2, j3, j4, j5, j6, j7, j8, j9;
    j1 = (abs(arr[0] - arr[1])>1) && (abs(arr[0] - arr[3]) > 1) && (abs(arr[0] - arr[4]) > 1)&&(abs(arr[0] - arr[5]) > 1);
    j2 = (abs(arr[1] - arr[2]) > 1) && (abs(arr[1] - arr[4]) > 1) && (abs(arr[1] - arr[5]) > 1) && (abs(arr[1] - arr[6]) > 1) ;
    j3 = (abs(arr[2] - arr[5]) > 1) && (abs(arr[2] - arr[6]) > 1);
    j4 = (abs(arr[3] - arr[4]) > 1) && (abs(arr[3] - arr[7]) > 1) && (abs(arr[3] - arr[8]) > 1);
    j5 = (abs(arr[4] - arr[5]) > 1) && (abs(arr[4] - arr[7]) > 1) && (abs(arr[4] - arr[8]) > 1) && (abs(arr[4] - arr[9]) > 1);
    j6 = (abs(arr[5] - arr[6]) > 1) && (abs(arr[5] - arr[8]) > 1) && (abs(arr[5] - arr[9]) > 1);
    j7 = (abs(arr[6] - arr[9]) > 1);
    j8 = abs(arr[7] - arr[8]) > 1;
    j9 = abs(arr[8] - arr[9]) > 1;
    if (j1&&j2&&j3&&j4&&j5&&j6&&j7&&j8&&j9)
        return true;
    else
        return false;
}

int main()
{
    int arr[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; //放数字的数组
    int ans = 0;
    while (next_permutation(arr, arr + 10))
    {
        if (judge(arr))
            ans++;
    }
    cout << ans;
    return 0;
}

答案:1580
标签:搜索、全排列

第7题 剪邮票

题目:
如图1, 有12张连在一起的12生肖的邮票。
现在你要从中剪下5张来,要求必须是连着的。
(仅仅连接一个角不算相连)
比如,图2,图3中,粉红色所示部分就是合格的剪取。
请你计算,一共有多少种不同的剪取方法。

图1

图2

图3

分析:这里用到一个技巧,设定了一个数组a[],其中有5个1,那么这个数组的每一个全排列就代表一种情况。check()函数将全排列的结果存入二维数组中,dfs()对每个格子进行深度优先遍历,如果遍历联通,就把数组相应位置置为0,每次遍历一个连通块就计次数,计数超过一标明块不连续。

补充:手动写全排列的框架函数如下:

#include <iostream>
using namespace std;

void f(int arr[], int k, int n)
{
    if (k == n)
    {
        for (int i = 0; i < n; i++)
            printf("%d ", arr[i]);
        cout << '\n';
        return;
    }
    for (int i = k + 1; i < n; i++)
    {
        int t = arr[i]; arr[i] = arr[k]; arr[k] = t;
        f(arr, k + 1, n);
        t = arr[i]; arr[i] = arr[k]; arr[k] = t;
    }
}

int main()
{
    int a[] = { 1, 2, 3};
    f(a, 0, 3);
    return 0;
}

由于本题解决方法很多,这里只给出思路,不给出解题过程。

答案:116
标签:全排列、联通问题

第8题 四平方定理

题目:
四平方和定理,又称为拉格朗日定理:
每个正整数都可以表示为至多4个正整数的平方和。
如果把0包括进去,就正好可以表示为4个数的平方和。
比如:
5 = 0^2 + 0^2 + 1^2 + 2^2
7 = 1^2 + 1^2 + 1^2 + 2^2
(^符号表示乘方的意思)
对于一个给定的正整数,可能存在多种平方和的表示法。
要求你对4个数排序:
0 <= a <= b <= c <= d
并对所有的可能表示法按 a,b,c,d 为联合主键升序排列,最后输出第一个表示法

分析:可以枚举a b c d,0<=a²<=N/4,0<=b²<=N/3,0<=c²<=N/2,0<=d²<=N/1。可以得到一部分分数。但是枚举数量过大,需要剪枝。这里用到了map映射数据结构,通过把c²+d²映射到字典里,可以直接查找到,不用遍历c和d从而减小复杂度,减少循环时间。如果在其中找到对应的c,直接可以算出d。然后满足要求之后直接输出即可,因为结果已经满足升序排列。
代码:

#include <iostream>
#include <cmath>
#include <map>
using namespace std;

int main()
{
    map<int, int> catche;
    int n;
    cin >> n;
    for (int c = 0; c*c <= n / 2; c++)
    {
        for (int d = c; c*c + d * d <= n; d++) //这里的范围可以试着改变一下
        {
            catche[c*c + d * d] = c; //映射较小的值,另一个数可以算出
        }
    }

    for (int a = 0; a*a <= n / 4; a++)
    {
        for (int b = a; a*a + b * b <= n / 2; b++)
        {
            if (catche.find(n - a * a - b * b) != catche.end()) //如果找到映射
            {
                int c = catche[n - a * a - b * b]; //取出c
                int d = (int)sqrt(n - a * a - b * b - c * c); //计算d
                cout << a << " " << b << " " << c << " " << d << endl; //输出结果
                return 0;
            }
        }
    }
    return 0;
}

标签:暴力枚举、优化

第9题:交换瓶子

题目:
有N个瓶子,编号 1 ~ N,放在架子上。
比如有5个瓶子:
2 1 3 5 4
要求每次拿起2个瓶子,交换它们的位置。
经过若干次后,使得瓶子的序号为:
1 2 3 4 5
对于这么简单的情况,显然,至少需要交换2次就可以复位。
如果瓶子更多呢?你可以通过编程来解决。
输入格式为:
第一行: 一个正整数N(N<10000), 表示瓶子的数目
第二行:N个正整数,用空格分开,表示瓶子目前的排列情况。
输出数据为一行一个正整数,表示至少交换多少次,才能完成排序。
分析:其实,我认为这个题就是找逆序对,全部搜索即可。但是还有一种方法“归位”,不在位置上就交换,可以较高效地排序。
代码:

#include <iostream>
#include <cmath>
#include <map>
using namespace std;
int a[10001];

int pos(int num, int n)
{
    for (int i = 1; i <= n; i++)
    {
        if (a[i] == num)
            return i;
    }
    return -1;
}

void swap(int i, int j) {
    int t = a[i];
    a[i] = a[j];
    a[j] = t;
}

int main()
{
    int n; //瓶子数
    int ans = 0; //答案数
    cin >> n;
    for (int i = 1; i <= n; i++)
        cin >> a[i]; //输入瓶子位置
    for (int i = 1; i <= n; i++)
    {
        if (a[i] == i)
            continue;
        else {
            swap(i, pos(a[i], n));
            ans++;
        }
    }
    cout << ans;
    return 0;
}

标签:暴力枚举、逆序数

第10题:最大比例

题目:
X星球的某个大奖赛设了M级奖励。每个级别的奖金是一个正整数。
并且,相邻的两个级别间的比例是个固定值。
也就是说:所有级别的奖金数构成了一个等比数列。比如:
16,24,36,54
其等比值为:3/2
现在,我们随机调查了一些获奖者的奖金数。请你据此推算可能的最大的等比值。输入格式:
第一行为数字 N (0<N<100),表示接下的一行包含N个正整数
第二行N个正整数Xi(Xi<1 000 000 000 000),用空格分开。每个整数表示调查到的某人的奖金数额
要求输出:
一个形如A/B的分数,要求A、B互质。表示可能的最大比例系数
测试数据保证了输入格式正确,并且最大比例是存在的。
例如,输入:
3
1250 200 32程序应该输出:
25/4
再例如,输入:
4
3125 32 32 200
程序应该输出:
5/2
再例如,输入:
3
549755813888 524288 2
程序应该输出:
4/1

分析:这里新学到了一种算法,当知道两个数 x,y。 x=p^i y=p^j 那么我们怎么求这个p呢? 下面介绍一种类似辗转相除法的算法,做这题这个是关键。

我们假设这个最大的公比是q=(q1/q2)(q1,q2均为正整数且互质)

我们将这个序列按照升序排列之后,a1,a2,a3......an,可以得出a2/a1=q1x1/q2x1, a3/a2=q1x2/q2x2......由于这个q一定是存在的,所以现在的问题就是分别找到分子和分母,既然前提假设了q是确定的,那么q1,q2也一定是确定的,要想从 q1x1,q1x2,......q1xn中找到q1,我们可以利用类似于求gcd的辗转相除法,我们将所有的ai/ai-1进行约分确立分子和分母,然后分别对所有的分子和分母进行辗转相除,最后约分输出就好了。还要注意的一点就是一定要对数组去重= =,不然会认为存在一个qx==1。
代码:

#include <iostream>
#include <algorithm>
#include "Year_2013_7.h"
using namespace std;

long long qgcd(long long a, long long b)
{
    if (a == b) return a;
    else
    {
        return qgcd(min(b / a, a), max(b / a, a));
    }
}

long long gcd(long long a, long long b)
{
    return b ? gcd(b, a%b) : a;
}

int main()
{
    long long a[101], b[101], x[101], n, N;
    cin >> n; //输入数字个数
    for (int i = 1; i <= n; i++)
        cin >> x[i]; //输入数字内容
    sort(x + 1, x + 1 + n); //对数字进行排序
    for (int i = 2; i <= n; i++) //去重
    {
        if (x[i] == x[i - 1]) {
            for (int j = i; j <= n; j++)
                x[j - 1] = x[j];
            n--;
        }
    }
    for (int i = 2; i <= n; i++)//存储所有的相邻数字的比例
    {
        a[i] = x[i];
        b[i] = x[i - 1];
        int p = gcd(x[i], x[i - 1]);
        a[i] /= p;
        b[i] /= p;
    }
    int A = a[2], B = b[2];
    for (int i = 3; i <= n; i++)
    {
        A = qgcd(A, a[i]);
        B = qgcd(B, b[i]);
    }
    cout << A <<" "<< B;
    return 0;
 }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,723评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,485评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,998评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,323评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,355评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,079评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,389评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,019评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,519评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,971评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,100评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,738评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,293评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,289评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,517评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,547评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,834评论 2 345