蓝桥杯2018年第九届C/C++ B组省赛习题题解

目录

第一题:第几天(手算)

第二题:明码(模拟)

第三题:乘积尾零(数学)

第四题:测试次数(dp)

第五题:快速排序(数据结构)

第六题:递增三元组(二段二分)

第七题:螺旋折线(数学)

第八题:日志统计(模拟)

第九题:全球变暖(bfs)

第十题:乘积最大(数学)


题目来源:2018年第九届C/C++ B组蓝桥杯省赛真题_戎码一生的博客-CSDN博客_第九届蓝桥杯c语言b组真题

第一题:第几天(手算)

#include<iostream>
using namespace std;
bool run(int year)
{
	return (year % 400 == 0 || (year % 4 == 0 && year % 100 != 0));
}

int main()
{
	int year = 2000;
	if (run(year)) cout << 31 + 29 + 31 + 30 + 4;//125
	else cout << 31 + 28 + 31 + 30 + 4;

	return 0;
}

第二题:明码(模拟)

题解来源:

第九届蓝桥杯 ——明码_业余算法学徒的博客-CSDN博客

 

 下图为:题目的输入

4 0 4 0 4 0 4 32 -1 -16 4 32 4 32 4 32 4 32 4 32 8 32 8 32 16 34 16 34 32 30 -64 0
16 64 16 64 34 68 127 126 66 -124 67 4 66 4 66 -124 126 100 66 36 66 4 66 4 66 4 126 4 66 40 0 16
4 0 4 0 4 0 4 32 -1 -16 4 32 4 32 4 32 4 32 4 32 8 32 8 32 16 34 16 34 32 30 -64 0
0 -128 64 -128 48 -128 17 8 1 -4 2 8 8 80 16 64 32 64 -32 64 32 -96 32 -96 33 16 34 8 36 14 40 4
4 0 3 0 1 0 0 4 -1 -2 4 0 4 16 7 -8 4 16 4 16 4 16 8 16 8 16 16 16 32 -96 64 64
16 64 20 72 62 -4 73 32 5 16 1 0 63 -8 1 0 -1 -2 0 64 0 80 63 -8 8 64 4 64 1 64 0 -128
0 16 63 -8 1 0 1 0 1 0 1 4 -1 -2 1 0 1 0 1 0 1 0 1 0 1 0 1 0 5 0 2 0
2 0 2 0 7 -16 8 32 24 64 37 -128 2 -128 12 -128 113 -4 2 8 12 16 18 32 33 -64 1 0 14 0 112 0
1 0 1 0 1 0 9 32 9 16 17 12 17 4 33 16 65 16 1 32 1 64 0 -128 1 0 2 0 12 0 112 0
0 0 0 0 7 -16 24 24 48 12 56 12 0 56 0 -32 0 -64 0 -128 0 0 0 0 1 -128 3 -64 1 -128 0 0
#include <iostream>
using namespace std;

int main()
{
	for (int i = 0; i < 320; i++)//每个汉字32个字节,一共有10个汉字,所以枚举320
	{
		int x;
		cin >> x;
		for (int j = 7; j >= 0; j--)//一个字节有8位信息,依次枚举,如果是:1,那么就打出"*"
		{
			if (x >> j & 1) cout << "*";
			else cout << " ";
		}
		if (i % 2 == 1) cout << endl;//每行一共两个字节
	}

	return 0;
}

 输出是:

 所以这道填空题要我们填写的是:(九的九次方是多少)

答案为:387420489(用long long 存即可)


第三题:乘积尾零(数学)

核心思路:

求末尾零的个数,那么就要思考这个末尾零是怎么产生的:

2*5=10               4*5=20                6*5=30                8*5=10

那么可以发现,问题已经转为了:求这100个数字中,末尾为偶数,或末尾为5的最小值即可

为什么是最小值???

因为如果有多个5,但是已经没有末尾为偶数的数与它进行匹配了,那么它也是凑不出末尾0的,所以我们这里取的是两者中的最小值即可

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

int main()
{
	int num5 = 0;
	int num2 = 0;
	for (int i = 0; i < 100; i++)
	{
		int x;
		cin >> x;

		if (x == 0)
		{
			cout << "0" << endl;
			return 0;
		}
		
		while (1)
		{
			if (x % 5 == 0)
			{
				num5++;
				x /= 5;
			}
			else if (x % 2 == 0)
			{
				num2++;
				x /= 2;
			}
			else break;
		}
	}

	cout << min(num2, num5) << endl;

	return 0;
}


第四题:测试次数(dp)

题解来自:

第九届蓝桥杯——测试次数_业余算法学徒的博客-CSDN博客_蓝桥杯测试次数

 dp思路

(1)dp数组的含义:有 i 部手机且楼数为 j 时,最坏运气下的最少测试次数

(2)dp数组的属性:最小次数

(3)dp数组的递推公式:

既然要测试,那么对于手机而言,则有在第K楼层的两种状态:

<1>手机坏了,那么一定是向下楼层进行搜索

根据含义,那么手机数量是必然要-1的(因为手机摔坏了一部)

那么对于最坏的运气,还要向下搜索k-1个楼层

所以对于该情况的状态转移方程为:f[i-1][k-1]+1

<2>手机没坏,那么一定是向上楼层进行搜索

根据含义,手机数量不发生变化(因为手机没有损坏)

那么对于最坏的情况,还要向上搜索j-k个楼层

所以对于该情况的状态转移方程为:f[i][j-k]+1

那么根据题目所求:最坏的运气下的最少测试次数,那么必然最外层一定是min

那么对于这两种情况呢???,答案是取max,因为这个是运气成分,要假设自己是最倒霉的,但是根据可以根据恰当的策略可以使得聪明的倒霉的我们得到一个满意的答案

(4)dp数组的初始化:

根据含义:假设现在身处第j楼层,那么测试的最坏运气就是j(向下测试,直到第一层知道结果)

(5)dp数组的遍历顺序:

无特殊要求,正序两层for即可

(6)

dp数组的返回值,按照题目所说的:3部手机,1000层高度即可

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

int f[5][1005];

int main()
{
	for (int i = 1; i <= 3; i++)
		for (int j = 1; j <= 1000; j++)
			f[i][j] = j;               // 无论有几部手机,运气最差时的测试次数就是楼层的高度                         
									   // (第一部手机从第一层摔到最后一层,都不坏)                        

	for (int i = 2; i <= 3; i++)//最少两部手机才能测出抗摔程度
		for (int j = 1; j <= 1000; j++)
			for (int k = 1; k < j; k++)//向下枚举
				f[i][j] = min(f[i][j], max(f[i - 1][k - 1], f[i][j - k]) + 1);  // min 表示最佳策略,max 表示最差运气 

	cout << f[3][1000] << endl;
	return 0;
}

小提示:注意题目中的j是总楼层的高度,k是当前楼层的高度,注意区分


第五题:快速排序(数据结构)

题目描述
以下代码可以从数组a[]中找出第k小的元素。
它使用了类似快速排序中的分治算法,期望时间复杂度是O(N)的。
请仔细阅读分析源码,填写划线部分缺失的内容。

#include <stdio.h>

int quick_select(int a[], int l, int r, int k) {
	int p = rand() % (r - l + 1) + l;
	int x = a[p];
	{int t = a[p]; a[p] = a[r]; a[r] = t;}
	int i = l, j = r;
	while(i < j) {
		while(i < j && a[i] < x) i++;
		if(i < j) {
			a[j] = a[i];
			j--;
		}
		while(i < j && a[j] > x) j--;
		if(i < j) {
			a[i] = a[j];
			i++;
		}
	}
	a[i] = x;
	p = i;
	if(i - l + 1 == k) return a[i];
	if(i - l + 1 < k) return quick_select( _____________________________ ); //填空
	else return quick_select(a, l, i - 1, k);
}
	
int main()
{
	int a[] = {1, 4, 2, 8, 5, 7, 23, 58, 16, 27, 55, 13, 26, 24, 12};
	printf("%d\n", quick_select(a, 0, 14, 5));
	return 0;
}

填空题:

写过快速排序的都知道,思路其实是分治递归,不断分裂左右区间并进行压缩

那么对于本题而言,看到函数的参数,就应该知道:

第一个参数是数组名,第二个参数是区间的左端点,第三个是区间的右端点,第四个暂时不知道

那么看到该空的下一行:else return quick_select(a, l, i - 1, k);

马上反应过来:这时枚举的右区间,那么对于上一行即(填空的该行)

就应该枚举左区间,对此前三个参数都已经确定:(a,i,r);

因为是求第k小的数,快排默认是升序的,所以对于左区间而言,正序寻找的第k个数,即是该组数据中第k小的数,

所以对于右区间而言,我们需要知道目标数,相对于右区间的左端点是正序的第几个数:

那么则有        k-(i-l+1)   -->>   第k小的数-(左区间的长度)得到它在右区间左端的正序位置

所以答案为:                         (a, i+1, r, k-(i-l+1))
 


第六题:递增三元组(二段二分)

活动 - AcWing

 

 这个本人之前写过详解了,不再赘述

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long LL;
const int N = 1e5 + 10;
int a[N], b[N], c[N];
int main()
{
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n; i++)  scanf("%d", &a[i]);
    for (int i = 0; i < n; i++)  scanf("%d", &b[i]);
    for (int i = 0; i < n; i++)  scanf("%d", &c[i]);
    sort(a, a + n);  //二分需要满足单调性
    //sort(b, b + n);
    sort(c, c + n);
    LL res = 0;  //答案可能会很大,会爆int
    for (int i = 0; i < n; i++)
    {
        int l = 0, r = n - 1;  //二分查找a数组中最后一个小于b[i]的数的下标
        while (l < r)
        {
            int mid = (l + r + 1) / 2;
            if (a[mid] < b[i])   l = mid;
            else   r = mid - 1;
        }
        if (a[l] >= b[i])   //如果未找到小于b[i]的数,将x标记为-1,后续计算时 x+1==0
        {
            l = -1;
        }
        int x = l;
        l = 0, r = n - 1;
        while (l < r)
        {
            int mid = (l + r) / 2;
            if (c[mid] > b[i])   r = mid;
            else  l = mid + 1;
        }
        if (c[l] <= b[i])   //如果未找到大于b[i]的数,将y标记为n,后续计算时 n-y==0;
        {
            r = n;
        }
        int y = r;
        res += (LL)(x + 1) * (n - y);
    }
    printf("%lld\n", res);
    return 0;
}

第七题:螺旋折线(数学)

活动 - AcWing

 

#include<iostream>
using namespace std;
typedef long long int LL;
LL X, Y;
int main()
{
    LL len = 0;
    cin >> X >> Y;
    if (Y >= -X)   //判断上下区
    {
        if (Y > X)     //判断上半区的上下区
        {
            len = 4 * Y * Y;
            len = len - abs(X - Y);
        }
        else
        {
            len = 4 * X * X;
            len = len + abs(X - Y);
        }
    }
    else {
        if (Y > X + 1)   //判断下半区的上下区
        {
            len = (abs(2 * X) - 1) * (abs(2 * X) - 1);
            len += (Y - X) - 1;
        }
        else
        {
            len = (abs(2 * Y) + 1) * (abs(2 * Y) + 1);
            len -= (X - Y + 1);
        }
    }
    cout << len;
    return 0;
}

作者:GRID
链接:https ://www.acwing.com/solution/content/24715/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

第八题:日志统计(模拟)

活动 - AcWing

 

 详细模拟过程已经写过-->>不再赘述

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define x first//时刻
#define y second//编号
typedef pair<int,int> PII;
const int N=100010;
PII nums[N];
int cnt[N];
int st[N];

int main()
{
    int n,d,k;
    scanf("%d%d%d",&n,&d,&k);
    
    for(int i=0;i<n;i++) scanf("%d%d",&nums[i].x,&nums[i].y);
    
    sort(nums,nums+n);
    
    for(int i=0,j=0;i<n;i++)
    {
        int t=nums[i].y;
        cnt[t]++;
        
        while(nums[i].x-nums[j].x>=d)
        {
            cnt[nums[j].y]--;
            j++;
        }
        if(cnt[t]>=k) st[t]=true;
    }
    
    for(int i=0;i<=N;i++)
    {
        if(st[i])
        {
            printf("%d\n",i);
        }
    }
    
    return 0;
}

第九题:全球变暖(bfs)

 

 已详细写过-->>不再赘述

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define x first
#define y second
using namespace std;
typedef pair<int, int> PII;
const int N = 1010;
int n;
char g[N][N];
bool st[N][N];
PII q[N * N];
int dx[4] = { -1,0,1,0 };
int dy[4] = { 0,1,0,-1 };
// total : 岛屿的板块的数量  bound:邻接海洋的岛屿板块的数量
void bfs(int sx, int sy, int& total, int& bound)
{
	int hh = 0;
	int tt = 0;
	q[0] = { sx,sy };
	st[sx][sy] = true;

	while (hh <= tt)
	{
		PII t = q[hh++];

		total++;
		bool is_bound = false;
		for (int i = 0; i < 4; i++)
		{
			int x = t.x + dx[i];
			int y = t.y + dy[i];
			if (x < 0 || x >= n || y < 0 || y >= n) continue;
			if (st[x][y]) continue;//如果已经走过了
			if (g[x][y] == '.')//上下左右之中有一个为海
			{
				is_bound = true;
				continue;
			}
			q[++tt] = { x,y };
			st[x][y] = true;
		}
		if (is_bound) bound++;//只要上下左右之中有一个为海,那么它就是边界
	}
}

int main()
{
	cin >> n;

	for (int i = 0; i < n; i++) cin >> g[i];

	int cnt = 0;
	for(int i=0;i<n;i++)
		for(int j=0;j<n;j++)
			if (!st[i][j] && g[i][j] == '#')
			{
				int total = 0;
				int bound = 0;
				bfs(i, j, total, bound);
				if (total == bound) cnt++;
			}
	cout << cnt << endl;

	return 0;
}

第十题:乘积最大(数学)

活动 - AcWing

 

 详细过程已写过-->>不再赘述

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

typedef long long LL;

const int N = 100010, mod = 1000000009;

int n, k;
int a[N];

int main()
{
    scanf("%d%d", &n, &k);
    for (int i = 0; i < n; i++) scanf("%d", &a[i]);
    sort(a, a + n);

    int res = 1;
    int l = 0, r = n - 1;
    int sign = 1;
    if (k % 2)
    {
        res = a[r--];
        k--;
        if (res < 0) sign = -1;
    }
    while (k)
    {
        LL x = (LL)a[l] * a[l + 1], y = (LL)a[r - 1] * a[r];
        if (x * sign > y * sign)
        {
            res = x % mod * res % mod;
            l += 2;
        }
        else
        {
            res = y % mod * res % mod;
            r -= 2;
        }
        k -= 2;
    }

    printf("%d\n", res);

    return 0;
}

  • 4
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值