第八届蓝桥杯真题大学生C++b组

第八届蓝桥杯真题题解

  • 1. 购物单
  • 2. 等差素数序列
  • 3. 承压计算
  • 4. 方格分割
  • 5. 取数位
  • 6.最大公共子串
  • 7. 日期问题
  • 8. 凑包子数
  • 9. 分巧克力
    • (1)暴力
    • (2)二分
  • 10. k倍区间

1. 购物单

小明刚刚找到工作,老板人很好,只是老板夫人很爱购物。老板忙的时候经常让小明帮忙到商场代为购物。小明很厌烦,但又不好推辞。这不,XX大促销又来了!老板夫人开出了长长的购物单,都是有打折优惠的。小明也有个怪癖,不到万不得已,从不刷卡,直接现金搞定。现在小明很心烦,请你帮他计算一下,需要从取款机上取多少现金,才能搞定这次购物。取款机只能提供100元面额的纸币。小明想尽可能少取些现金,够用就行了。你的任务是计算出,小明最少需要取多少现金。
以下是让人头疼的购物单,为了保护隐私,物品名称被隐藏了。

**** 180.90 88折
**** 10.25 65折
**** 56.14 9折
**** 104.65 9折
**** 100.30 88折
**** 297.15 半价
**** 26.75 65折
**** 130.62 半价
**** 240.28 58折
**** 270.62 8折
**** 115.87 88折
**** 247.34 95折
**** 73.21 9折
**** 101.00 半价
**** 79.54 半价
**** 278.44 7折
**** 199.26 半价
**** 12.97 9折
**** 166.30 78折
**** 125.50 58折
**** 84.98 9折
**** 113.35 68折
**** 166.57 半价
**** 42.56 9折
**** 81.90 95折
**** 131.78 8折
**** 255.89 78折
**** 109.17 9折
**** 146.69 68折
**** 139.33 65折
**** 141.16 78折
**** 154.74 8折
**** 59.42 8折
**** 85.44 68折
**** 293.70 88折
**** 261.79 65折
**** 11.30 88折
**** 268.27 58折
**** 128.29 88折
**** 251.03 8折
**** 208.39 75折
**** 128.88 75折
**** 62.06 9折
**** 225.87 75折
**** 12.89 75折
**** 34.28 75折
**** 62.16 58折
**** 129.12 半价
**** 218.37 半价
**** 289.69 8折

需要说明的是,88折指的是按标价的88%计算,而8折是按80%计算,余者类推。
特别地,半价是按50%计算。

请提交小明要从取款机上提取的金额,单位是元。
答案是一个整数,类似4300的样子,结尾必然是00,不要填写任何多余的内容。
采用记事本的查找与替换功能将其转换,再复制粘贴到Excel表中使用函数算和
180.90 88
10.25 65
56.14 90
104.65 90
100.30 88
297.15 50
26.75 65
130.62 50
240.28 58
270.62 80
115.87 88
247.34 95
73.21 90
101.00 50
79.54 50
278.44 70
199.26 50
12.97 90
166.30 78
125.50 58
84.98 90
113.35 68
166.57 50
42.56 90
81.90 95
131.78 80
255.89 78
109.17 90
146.69 68
139.33 65
141.16 78
154.74 80
59.42 80
85.44 68
293.70 88
261.79 65
11.30 88
268.27 58
128.29 88
251.03 80
208.39 75
128.88 75
62.06 90
225.87 75
12.89 75
34.28 75
62.16 58
129.12 50
218.37 50
289.69 80

输出结果是5118,按题目要求最终答案应该是5200

2. 等差素数序列

2,3,5,7,11,13,…是素数序列。
类似:7,37,67,97,127,157 这样完全由素数组成的等差数列,叫等差素数数列。
上边的数列公差为30,长度为6。

2004年,格林与华人陶哲轩合作证明了:存在任意长度的素数等差数列。
这是数论领域一项惊人的成果!

有这一理论为基础,请你借助手中的计算机,满怀信心地搜索:

长度为10的等差素数列,其公差最小值是多少?
这道题主要是考筛素数和枚举,首先线性筛素数

for(ll i = 2; i < N; i++)
    {
        if(!isprime[i])//如果是素数
        {
            prime[k++] = i;//加入prime数组
            for(ll j = i ; j * i < N; j++)
            {
                isprime[j * i] = 1;//i的倍数都不是素数
            }
        }
    }

再让公差从小到大枚举,注意公差不能太大否则会产生数组越界

#include 

using namespace std;
typedef long long ll;
const ll N = 1000010;
int isprime[N] = {1,1,0};
int prime[N],k = 0;
int main()
{
    for(ll i = 2; i < N; i++)
    {
        if(!isprime[i])//如果是素数
        {
            prime[k++] = i;//加入prime数组
            for(ll j = i ; j * i < N; j++)
            {
                isprime[j * i] = 1;//i的倍数都不是素数
            }
        }
    }
    for(ll i = 1 ; i < k ; i++)
    {
        for(ll d = 2; d < 10000; d += 2)/*公差*/
        {
            int num = prime[i],g/*首项*/;
            for(g = 0 ; g < 10; g++)
            {
                if(isprime[num + g * d])/*ak不是素数*/
                {
                   break;
                }
            }
            if(g == 10)/*十项都是素数*/
            {
                cout << d << endl;
                return 0;
            }
        }
    }

    return 0;
}

最终答案:210

3. 承压计算

X星球的高科技实验室中整齐地堆放着某批珍贵金属原料。

每块金属原料的外形、尺寸完全一致,但重量不同。
金属材料被严格地堆放成金字塔形。
7
5 8
7 8 8
9 2 7 2
8 1 4 9 1
8 1 8 8 4 1
7 9 6 1 4 5 4
5 6 5 5 6 9 5 6
5 5 4 7 9 3 5 5 1
7 5 7 9 7 4 7 3 3 1
4 6 4 5 5 8 8 3 2 4 3
1 1 3 3 1 6 6 5 5 4 4 2
9 9 9 2 1 9 1 9 2 9 5 7 9
4 3 3 7 7 9 3 6 1 3 8 8 3 7
3 6 8 1 5 3 9 5 8 3 8 1 8 3 3
8 3 2 3 3 5 5 8 5 4 2 8 6 7 6 9
8 1 8 1 8 4 6 2 2 1 7 9 4 2 3 3 4
2 8 4 2 2 9 9 2 8 3 4 9 6 3 9 4 6 9
7 9 7 4 9 7 6 6 2 8 9 4 1 8 1 7 2 1 6
9 2 8 6 4 2 7 9 5 4 1 2 5 1 7 3 9 8 3 3
5 2 1 6 7 9 3 2 8 9 5 5 6 6 6 2 1 8 7 9 9
6 7 1 8 8 7 5 3 6 5 4 7 3 4 6 7 8 1 3 2 7 4
2 2 6 3 5 3 4 9 2 4 5 7 6 6 3 2 7 2 4 8 5 5 4
7 4 4 5 8 3 3 8 1 8 6 3 2 1 6 2 6 4 6 3 8 2 9 6
1 2 4 1 3 3 5 3 4 9 6 3 8 6 5 9 1 5 3 2 6 8 8 5 3
2 2 7 9 3 3 2 8 6 9 8 4 4 9 5 8 2 6 3 4 8 4 9 3 8 8
7 7 7 9 7 5 2 7 9 2 5 1 9 2 6 5 3 9 3 5 7 3 5 4 2 8 9
7 7 6 6 8 7 5 5 8 2 4 7 7 4 7 2 6 9 2 1 8 2 9 8 5 7 3 6
5 9 4 5 5 7 5 5 6 3 5 3 9 5 8 9 5 4 1 2 6 1 4 3 5 3 2 4 1
X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X

其中的数字代表金属块的重量(计量单位较大)。
最下一层的X代表30台极高精度的电子秤。

假设每块原料的重量都十分精确地平均落在下方的两个金属块上,
最后,所有的金属块的重量都严格精确地平分落在最底层的电子秤上。
电子秤的计量单位很小,所以显示的数字很大。

工作人员发现,其中读数最小的电子秤的示数为:2086458231

请你推算出:读数最大的电子秤的示数为多少?
这道题类似于杨辉三角,将每个金属块平均分配给下面a[i+1][j]和a[i+1][j+1]两个金属块,最后输出最大即可,注意最后单位换算
最终答案:2665192664

#include 
#include 
using namespace std;
double a[66][66];
int main()
{
    for(int i = 1; i <= 29; i++)
    {
        for(int j = 1 ; j <= i; j++)
        {
            cin >> a[i][j];
        }
    }
    for(int i = 1; i <= 29 ; i++)
    {
        for(int j = 1 ; j <= i; j++)
        {
            a[i+1][j] += a[i][j] / 2;
            a[i+1][j+1] += a[i][j] / 2;
        }
    }
    double maxnum = a[30][1];
    double minnum = a[30][1];
    for(int i = 1 ; i <= 30; i++)/*遍历最后一层找最大值和最小值*/
    {
        if(a[30][i] > maxnum)
        {
            maxnum = a[30][i];
        }
        else
        {
            minnum = a[30][i];
        }
    }
    //cout << maxnum <<" " << minnum << endl;
    printf("%.0lf",2086458231 * (maxnum /minnum));
    /*换算单位*/
    return 0;
}

4. 方格分割

由于篇幅多所以写到另一个博客https://blog.csdn.net/leslie___/article/details/105658420

5. 取数位

求1个整数的第k位数字有很多种方法。
以下的方法就是一种。
水题

// 求x用10进制表示时的数位长度
int len(int x){
if(x<10) return 1;
return len(x/10)+1;
}

// 取x的第k位数字
int f(int x, int k){
if(len(x)-k==0) return x%10;
return x / 10_______; //填空
}

int main()
{
int x = 23574;
printf("%d\n", f(x,3));
return 0;
}

对于题目中的测试数据,应该打印5。

6.最大公共子串

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

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

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

请分析该解法的思路,并补全划线部分缺失的代码。
画个矩阵就懂了

#include 
#include 

#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] = a[i-1][j-1] + 1;  //填空
				if(a[i][j] > max) max = a[i][j];
			}
		}
	}
	
	return max;
}

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

7. 日期问题

小明正在整理一批历史文献。这些历史文献中出现了很多日期。小明知道这些日期都在1960年1月1日至2059年12月31日。令小明头疼的是,这些日期采用的格式非常不统一,有采用年/月/日的,有采用月/日/年的,还有采用日/月/年的。更加麻烦的是,年份也都省略了前两位,使得文献上的一个日期,存在很多可能的日期与其对应。

比如02/03/04,可能是2002年03月04日、2004年02月03日或2004年03月02日。

给出一个文献上的日期,你能帮助小明判断有哪些可能的日期对其对应吗?

输入
一个日期,格式是"AA/BB/CC"。 (0 <= A, B, C <= 9)

输入
输出若干个不相同的日期,每个日期一行,格式是"yyyy-MM-dd"。多个日期按从早到晚排列。

样例输入
02/03/04

样例输出
2002-03-04
2004-02-03
2004-03-02
思路:将每个月的天数分平年和闰年存到二维数组里,然后将输入的三个数按照不同次序来判断:1.年份是否在1960-2059之间,2.年份满足后判断平闰年,3.判断每个顺序是否满足日期的规定,最后存入set中按顺序输出

#include 
#include 
#include 
using namespace std;
int m[2][13] = {0,31,28,31,30,31,30,31,31,30,31,30,31,
                0,31,29,31,30,31,30,31,31,30,31,30,31};
set<string>s;
void check(int a[])
{
    char c[15];
    int y = 1900 + a[0],f = 0;
    if(y < 1960) y += 100;
    if((y % 4 == 0 && y % 100 != 0) || ( y % 400 == 0)) f = 1;
    if(a[1] >= 1 && a[1] <= 12)
    {
        if(a[2] >= 1 && a[2] <= m[f][a[1]])
        {
            sprintf(c,"%d-%02d-%02d",y,a[1],a[2]);
            s.insert(c);
        }
    }
}
int main()
{
    int a[3];
    scanf("%d/%d/%d",&a[0],&a[1],&a[2]);
    check(a);
    int t;
    t = a[2],a[2] = a[1],a[1] = a[0];
    check(a);
    t = a[2];a[2] = a[1];a[1] = t;
    check(a);
    for(set<string>::iterator it = s.begin();it != s.end() ; it++) cout << *it << endl;
    return 0;
}

8. 凑包子数

小明几乎每天早晨都会在一家包子铺吃早餐。他发现这家包子铺有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

再例如,
输入:
2
4
6

程序应该输出:
INF

样例解释:
对于样例1,凑不出的数目包括:1, 2, 3, 6, 7, 11。
对于样例2,所有奇数都凑不出来,所以有无限多个。

这道题应用了扩展欧几里得的知识
首先观察这道题,对于4,5不能凑出的包子数:1,2,3,6,7,11,而对于4,6所能凑出的包子数都是2的倍数,因此奇数都不能凑出所以才输出INF,根据这个就可以得到一个公式:
ax + by = c,我们所要求的就是当x 和 y为都为非负整数时不能求出的解,根据这个就能想到exgcd:ax + by = gcd(a,b)即ax + by = c若有解那么c一定是gcd(a,b)的倍数,因此如果gcd(a,b) = 1那么ax + by = c,c可以取任何非负整数,但是x和y可能为负所以就会有得不到解的情况,但如果不为1就会有INF个解不出来的解,这道题就变成了判断输入的整数中他们的gcd是否等于1,如果不等于1就输出INF否则就来找x和y都为非负整数时求不出来的解

#include 

using namespace std;
const int N = 10010;
int a[N];
bool b[N];
int gcd(int a,int b)
{
    return b == 0 ? a : gcd(b,a % b);
}
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;
    }
    else
    {
        b[0] = true;
    }
    for(int i = 1; i <= n ; i++)/*不禁让人联想到了第二题筛素数的方法*/
    {
        for(int j = 0; j + a[i] < N; j++)
        {
            if(b[j])
            {
                b[j + a[i]] = true;/*能凑出来的包子数都标记为true*/
            }
        }
    }
    int cnt = 0;
    for(int i = 0 ; i < N; i++)
    {
        if(b[i] == false)
        {
            cnt++;
        }
    }
    cout << cnt << endl;
    return 0;
}

9. 分巧克力

儿童节那天有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
首先要明白一点,当边长为h[i],w[i]时,若要将其分为边长为j的小正方形一共可以分(h[i] / j) * (w[i] / j)个

(1)暴力

暴力解法就是在输入每个边长的时候找到最大边长,并将其赋值给m,再令i = m,i–,当(h[j] / i) * (w[j] / i) >= k时就找到了符合条件的最大边输出i即可,从最大值开始暴力找,只要符合条件就输出保证了边最大

#include 

using namespace std;
const int N = 1e5 + 5;
int h[N],w[N];
int main()
{
    int n,k;
    cin >> n >> k;
    int maxnum = -1;
    for(int i = 0 ; i < n ; i++)
    {
        cin >> h[i] >> w[i];
        maxnum = max(h[i],w[i]);
    }
    int m = maxnum;/*找到最大边长并赋值给m*/
    for(int i = m; i >= 1; i--)
    {
    	int sum = 0;
        for(int j = 0 ; j < n ; j++)
        {
           sum += ((h[j] / i) *(w[j] / i));
        }
        if(sum >= k)/*如果符合条件*/
        {
            cout << i << endl;
            break;
        }
    }
    return 0;
}

(2)二分

和暴力思想差不多,只不过采用了二分查找效率更高

#include 

using namespace std;
const int N = 1e5 + 5;
int h[N],w[N];
int main()
{
    int n,k;
    cin >> n >> k;
    int maxnum = -1;
    for(int i = 0 ; i < n ; i++)
    {
        cin >> h[i] >> w[i];
        maxnum = max(h[i],w[i]);
    }
    int l = 1,r = maxnum,ans = 0;
    while(l <= r)
    {
        int mid = (l + r) / 2;
        int sum = 0;
        for(int i = 0 ; i < n ; i++)
        {
            sum += (h[i] / mid) * (w[i] / mid);
        }
        if(sum >= k)
        {
            l = mid + 1;
            ans = max(mid,ans);
        }
        else
        {
            r = mid - 1;
        }
    }
    cout << ans << endl;
    return 0;
}

10. k倍区间

标题: k倍区间

给定一个长度为N的数列,A1, A2, … AN,如果其中一段连续的子序列Ai, Ai+1, … Aj(i <= j)之和是K的倍数,我们就称这个区间[i, j]是K倍区间。

你能求出数列中总共有多少个K倍区间吗?

输入
第一行包含两个整数N和K。(1 <= N, K <= 100000)
以下N行每行包含一个整数Ai。(1 <= Ai <= 100000)

输出
输出一个整数,代表K倍区间的数目。

例如,
输入:
5 2
1
2
3
4
5

程序应该输出:
6

思路:这道题就是求1 2 3 4 5这些数字的组合中其和为k的倍数的组合个数

  • 首先将这n个数用前缀和处理,我们不难发现,L为起点,R为终点的区间中,满足条件的算式就是:(sum[R] - sum[L - 1]) % k = 0
  • 再将这个式子变形就是
    sum[R] % k = sum[L - 1] % k
    sum[R] ≡ sum[L-1](mod k)
  • 因此,这道题就变成了将前缀和的每一项 %k最终来找余数相同的个数
    比如题目中的例子
    1 2 3 4 5
    1 3 6 10 15:每一项的前缀和
    1 1 0 0 1:每一项的前缀和除以k的余数
    1和2的余数相同得到区间:[2,2]
    1和5的余数相同得到区间:[2,5]
    2和5的余数相同得到区间:[3,5]
    3和4的余数相同得到区间:[4,4]

    同时还要加上余数本身为0
    3的余数为0,得到区间:[1,3]
    4的余数为0,得到区间:[1,4]
#include 

using namespace std;
const int N = 1e5 + 5;
typedef long long ll;
ll a[N],bk[N];
int main()
{
    int n,k;
    cin >> n >> k;
    for(int i = 0 ; i < n ; i++)
    {
        cin >> a[i];
    }
    a[0] %= k;
    for(int i = 1; i < n ; i++)
    {
        a[i] = (a[i] + a[i - 1]) % k;
    }
    ll sum = 0;
    for(int i = 0 ; i < n ; i++)
    {
        sum += bk[a[i]]++;
        cout << sum << endl;
    }
    cout << sum + bk[0] << endl;
    return 0;
}

你可能感兴趣的:(蓝桥杯)