蓝桥杯2018第九届C语言B组省赛习题题解

蓝桥杯2018第九届C语言B组省赛习题题解

  • 习题A:第几天
    • 题意解析
  • 习题B:明码
    • 题意解析
    • 代码展示
  • 习题C:乘积尾零
    • 题意分析
    • 代码展示
    • 习题D:测试次数
    • 思路分析
    • 代码展示
  • 习题F 递增三元组
    • 题意分析
    • 代码展示
  • 习题G:螺旋折线
    • 题意分析
    • 代码展示
  • 习题H:日志统计
    • 思路分析
    • 代码展示

习题A:第几天

蓝桥杯2018第九届C语言B组省赛习题题解_第1张图片

题意解析

记得判断闰年,2020年是闰年,剩下的手算就行,直接加一下就好

习题B:明码

蓝桥杯2018第九届C语言B组省赛习题题解_第2张图片

题意解析

一个字节八位,这里题目的意思就是,给你十段数字,每一段数字有32个有符号整数,0是底色,不用管,每个汉字用32个字节表示,其实说白了,就是把一行的所有字符表示成二进制,人后按照排版的方式,安排好,就可以从一堆的0和1的字符中看出一个汉字,是个汉字就可以看懂题意了,
十进制转换成八位的二进制数
如果是0,就全部为0
如果大于0,首位是0,后面就是对应的数字的二进制
如果小于0,首位是1,后面

代码展示

#include 
using namespace std;

void toBinaryStr(int i,string &ans)
{
    if(i>=0){
        ans[0] = '-';
        for(int j=0;j<7;j++)
        {
            if(((i>>j) & 1) == 1)//验证一个数的二进制数的某一位是否是1
            {
                ans[8-j-1] = '1';//其实就是在比较右移几位是1,逆序填进去
            }
        }
    }
    else
    {
        ans[0] = '1';
        for(int j=0;j<7;j++)
        {
            if((((i+128)>>j) & 1 ) == 1)//八位,负数可以直接先加上2^7,然后直接按照正数计算(-1,就相当于127)
            {
                ans[8-j-1] = '1';
            }
        }
    }
}
int main()
{
    for(int i=0;i<10;i++){
        //下面的32个数字组成一个汉字
        for(int j=0;j<16;j++)
        {
            int x,y;
            cin>>x>>y;
            string xx = "--------",yy = "--------";//优化一下,背景的0看起来模糊,换成-
            toBinaryStr(x,xx);
            toBinaryStr(y,yy);//转换成二进制字符串
            cout<<xx+yy<<endl;
        }
        cout<<endl;
        cout<<"========================="<<endl;//分隔开,为了看清
    }

    long long ans = 9;
    for(int k=0;k<9;k++)
        ans *= 9;
    cout<<ans<<endl;
    return 0;
}

/*
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 
*/

习题C:乘积尾零

蓝桥杯2018第九届C语言B组省赛习题题解_第3张图片

题意分析

这道题,只要将每个数分解成2和5相乘的式子(如 30=523),当然不能分解的就不要管他了,最后统计出有多少个2和5,输出较少的数即可。之所以酱紫是很容易理解的,两个数相乘等于这两个数分解后得到的式子相乘,然后在1-9这九个数字中,只有2和5的配合才能产生0;

代码展示

#include 
#include

using namespace std;

int main()
{
    int data[105];
    for(int i=0;i<100;i++)
    {
        cin>>data[i];
    }
    int c2 = 0,c5 = 0;
    for(int i=0;i<100;i++)
    {
        int num = data[i];
        while(num %2 == 0){
            c2++;
            num/=2;
        }

        while(num%5==0)
        {
            c5++;
            num/=5;
        }
    }
    cout<<min(c2,c5)<<endl;
    return 0;
}

/*
5650 4542 3554 473 946 4114 3871 9073 90 4329
2758 7949 6113 5659 5245 7432 3051 4434 6704 3594
9937 1173 6866 3397 4759 7557 3070 2287 1453 9899
1486 5722 3135 1170 4014 5510 5120 729 2880 9019
2049 698 4582 4346 4427 646 9742 7340 1230 7683
5693 7015 6887 7381 4172 4341 2909 2027 7355 5649
6701 6645 1671 5978 2704 9926 295 3125 3878 6785
2066 4247 4800 1578 6652 4616 1113 6205 3264 2915
3966 5291 2904 1285 2193 1428 2265 8730 9436 7074
689 5510 8243 6114 337 4096 8199 7313 3685 211
*/

习题D:测试次数

x星球的居民脾气不太好,但好在他们生气的时候唯一的异常举动是:摔手机。
各大厂商也就纷纷推出各种耐摔型手机。x星球的质监局规定了手机必须经过耐摔测试,并且评定出一个耐摔指数来,之后才允许上市流通。

x星球有很多高耸入云的高塔,刚好可以用来做耐摔测试。塔的每一层高度都是一样的,与地球上稍有不同的是,他们的第一层不是地面,而是相当于我们的2楼。

如果手机从第7层扔下去没摔坏,但第8层摔坏了,则手机耐摔指数=7。
特别地,如果手机从第1层扔下去就坏了,则耐摔指数=0。
如果到了塔的最高层第n层扔没摔坏,则耐摔指数=n

为了减少测试次数,从每个厂家抽样3部手机参加测试。

某次测试的塔高为1000层,如果我们总是采用最佳策略,在最坏的运气下最多需要测试多少次才能确定手机的耐摔指数呢?

请填写这个最多测试次数。

注意:需要填写的是一个整数,不要填写任何多余内容。

思路分析

就是递推,但是不要陷入思维陷阱,最佳的策略不一定就是从中间开始噢,一定要把前几个分析一下从每一层开始,情况是好是坏分析一下,就可以得出结论。

代码展示

#include
#include
using namespace std;
//忘记思路看这个https://www.bilibili.com/video/BV1aK4y1v7mE?p=18&spm_id_from=pageDriver 
const int N = 1000;
int f1[N+1],f2[N+1],f3[N+1];//记录手机数为1,2,3时,随影各层的测试次数
int main()
{
	//1部手机的情况
	for(int i=1;i<=N;i++)
	{
		f1[i] = i;
	 } 
	
	//考虑2部手机的情况
	for(int i=1;i<=N;i++){
		int ans =  INT_MAX;
		//尝试1~i若干中方案,最终记录所有方案中次数最少的
		for(int j=1;j<=i;j++)//在j层扔第一个手机
		{
			//1 好的 2 坏了 
		    int _max =1 + max(f2[i-j], f1[j-1]);
			ans = min(ans,_max); 
		 } 
		 f2[i] = ans; 
	} 
	
	//考虑三部手机的情况
	for(int i=1;i<=N;i++){
		int ans =  INT_MAX;
		//尝试1~i若干中方案,最终记录所有方案中次数最少的
		for(int j=1;j<=i;j++)//在j层扔第一个手机
		{
			//1 好的 2 坏了 
		    int _max =1 + max(f3[i-j], f2[j-1]);
			ans = min(ans,_max); 
		 } 
		 f3[i] = ans; 
	} 
	cout<

习题F 递增三元组

蓝桥杯2018第九届C语言B组省赛习题题解_第4张图片

题意分析

其实这就是以前蓝桥杯称之为暴力杯的源头,题意简单易懂,哪怕最笨的人,也可以使用简单的三重循环拿点分数,这道题目的真正得分的点在于优化,我们可以先把三个数组按照从小到大的顺序排好序,然后因为b在三个数的中间,所以考虑问题的时候,不要单一的被题目牵着鼻子走,不要先从a考虑,你先从b开始考虑,对于每一个b[i],你可以在a中找到比b[i]小的数,在c中找到比b[i]大的数,设置一个p指针在a中,设置一个q指针在c中,这样随着b数组的逐渐增大,p,和 q会按照顺序改变,也就可以避免重复的计算。蓝桥杯2018第九届C语言B组省赛习题题解_第5张图片

代码展示

#include
#include
#include
using namespace std;

int n;
long long ans;
int main()
{
	cin>>n;
	int a[n],b[n],c[n];
	for(int i=0;i<n;i++)
	{
		cin>>a[i];
	}
	for(int i=0;i<n;i++)
	{
		cin>>b[i];
	}
	for(int i=0;i<n;i++)
	{
		cin>>c[i];
	}
	
	sort(a,a+n);//首先把他们从小到大排好序 
	sort(b,b+n);
	sort(c,c+n);
	
	int p = 0,q = 0;
	for(int i=0;i<n;i++)//以b数组的每一个数为基准,向前找a数组,向后找c数组 
	{
		while(p<n && a[p]< b[i]) p++;
		while(q<n && c[q] <= b[i]) q++;//q是比b[i]小的个数,所以才要加上=,推出比b[i]大的个数为n-q;
		ans += (long long) p*(n-q);
	}
	cout<<ans<<endl;
	return 0;
 } 

习题G:螺旋折线

蓝桥杯2018第九届C语言B组省赛习题题解_第6张图片

题意分析

看到图,如果直接暴力肯定超时,并且还可能会超int,所以,参考别人的https://www.bilibili.com/video/BV1aK4y1v7mE?p=20&spm_id_from=pageDriver方法
从左上角到右下角划一道线,这样就将整个图形长度分成了两个等差数列,上边的是4,8,12,
下边的是2,6,10,这样每次都以右下角为基准,算出右下角的长度,通过计算距离最近的右下角的长度,计算出他的合理长度

代码展示

#include
#include
#include
using namespace std;

//等差数列求和
#define sum(a0,n,d) (2*(a0) + ((n)-1)*(d))*(n)/2

typedef long long ll;

int main()
{
	ll x,y;
	cin>>x>>y;
	ll d = 0;//距离
	ll n = 0;//取第几圈
	if(y>0 && abs(x)<=y)
	{//最上方的倒三角的地方
		n = y;
		d = y - x + 2 * y;
	} 
	else if(x > 0 && abs(y) <= x)
	{//右侧的倒三角的位置
		n = x;
		d = y+x;
	}
	else if(y<=0 && x>= y-1 && x<= -y)
	{//下边的倒三角,但是是n-1行的噢,注意找到走的规律
		n = -y;
		d = -(-y-x);
	}
	else if(x <0 && y>= x+1 && y<= -x)
	{//左边的倒三角
		n = -x-1;
		d = -(y-x-1-2*x -1);
	}
	cout<<sum(1, 2*n, 1) * 2 - d<<endl;
	return 0;
 } 

习题H:日志统计

蓝桥杯2018第九届C语言B组省赛习题题解_第7张图片

思路分析

代码展示

#include
#include
#include
using namespace std;


int N,D,K;
struct R{
	int ts,td;//时刻及id 
};

bool cmp(R r1,R r2)
{//按照时刻对一个记录R做升序排序 
	return r1.ts < r2.ts;
}


void out(R r[])
{
	for(int i=0;i<N;i++)
	{
		cout<<r[i].ts<<" "<<r[i].td<<endl;
	}
}


int main()
{
	cin>>N>>D>>K;
	//存日志数据,ts-td分别是时刻及id,组合成对象,存储在vector 
	vector<R> records(N);
	map<int ,int> cnt;//记录id及其出现的次数
	//读取日志文件
	for(int i=0;i<N;i++)
	{
		cin>>records[i].ts >> records[i].td;
	} 
	 
	//用自定义比较器排序
	sort(records.begin(),records.end(),cmp);
	 
	int j = 0;//用于往后探的指针——尺取法的哨兵 
	set<int> answers;//记录结果id
	for(int i=0;i<N;i++){//i是尺取法的起点 
		while(j<N && records[j].ts - records[i].ts < D)
		{
			cnt[records[j].td]++;//该id的技术+1
			if(cnt[records[j].td] >= K)//如果计数满足条件 
				answers.insert(records[j].td);//id放入answer中 
			j++; 
		}
		cnt[records[i].td]--;//上一个 区间,td的技术要扣除,不干扰下一个区间的统计 
	}
	//输出答案 
	for(set<int>::iterator i =  answers.begin();i != answers.end();i++)
	{
		cout<<*i<<endl;
	}
	
	return 0;
}

你可能感兴趣的:(学习经验分享,算法)