第十二届蓝桥杯C/C++ B组答案+思路

A 空间

在这里插入图片描述
思路:

一个8位二进制整数占1个字节(1B)
一个32位二进制整数占4个字节(4B)
1MB=1024KB
1KB=1024B
ans=256 * 1024 * 1024 / 4

答案:

67108864

B 卡片

在这里插入图片描述
思路:

设一个大小为10的int数组N[10],下标代表卡片的数字,每个元素赋值为2021。
写一个bool函数,fun(int n) 判断n的每位数字的卡片数量>=0

注:不要忘了要减一

答案:

3181

#include<iostream>
using namespace std;
int N[10]={0};
bool fun(int n)
{
	while(n>0)
	{
		N[n%10]-=1;
		if(N[n%10]<0)
			return false;
		n/=10;
	}
	return true;
}
int main()
{
	for(int i=0;i<10;i++)
	{
		N[i]=2021;
	}
	int i;
	for(i=1;fun(i);i++);
	cout << i-1;
	return 0;
}

C 直线

在这里插入图片描述
思路:

利用两点式得直线方程一般式(y1-y2)x+(x2-x1)y+x1y2-x2y1=0
A=y1-y2;
B=x2-x1;
C=x1y2-x2y1;
遍历所有可能出现的直线(除了竖,横)
求出他们三个的最大公约数并将A,B,C化为最简
利用map映射排重

答案:

40257

/*
map内部存储机制是红黑树为基础,插入节点时 
必须按照大小比对后在一个合适的位置执行插入动作 
所以必须要有 "<"
*/ 

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

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

class ABC
{
public:
	int A;
	int B;
	int C;
	ABC()
	{
		A=0;B=0;C=0;
	}
	ABC(int a,int b,int c)
	{
		int bei;
		if(c==0)
			bei=gcd(a,b);
		else bei=gcd(gcd(a,b),c);
		A=a/bei;
		B=b/bei;
		C=c/bei;
	}
	bool operator ==(ABC a1)//想让他判断是否已经存在 ,但是好像不需要这个  
	{
		if(a1.A==A&&a1.B==B&&a1.C==C)
		{
			return true;
		}
		else return false;
	}
	
};
bool operator < (ABC a,ABC b) 
{
	if(a.A<b.A)
		return true;
	else if(a.A==b.A&&a.B<b.B)
		return true;
	else if(a.A==b.A&&a.B==b.B&&a.C<b.C)
		return true;
	return false;
}
map<ABC,int>mp;
int main()
{
	int x1,y1,x2,y2;
	int A,B,C;
	int ans=0;
	for(y2=1;y2<21;y2++)
	{
		for(x2=0;x2<20;x2++)
		{
			for(y1=0;y1<=y2;y1++)
			{
				for(x1=0;x1<20;x1++)
				{
					if(y1-y2==0)
						continue;
					else if(x2-x1==0)
						continue;
					A=y1-y2;
					B=x2-x1;
					C=y2*x1-x2*y1;
					ABC a(A,B,C);
					if(mp[a]!=1)
					{
						mp[a]=1;
						ans++;
					}
				}
			}
			
		}
	}
	cout << ans+21+20;
	return 0;
}

D 货物摆放

在这里插入图片描述
思路:

求出2021041820210418的所有因子
三重循环遍历即可

注:long long 最大可19位

答案:

2430

#include<iostream>
#include<math.h>
using namespace std;
int main()
{
	long long int n=2021041820210418;
	long long int N[130];//因子
	//int i_ans=0;
	int ans=0;
	int j=0;
	for(long long int i=1;i<=sqrt(n);i++)
	{
		if(n%i==0)
		{
			//i_ans+=2;
			N[j++]=i;
			N[j++]=n/i;
		}
	}
	//cout << i_ans;   i_ans=128 即有128个因子 
	for(int i=0;i<128;i++)
	{
		for(int j=0;j<128;j++)
		{
			for(int k=0;k<128;k++)
			{
				if(N[i]*N[j]*N[k]==2021041820210418)
					ans++;
			}
		}
	}
	cout << ans;
}

E 路径

在这里插入图片描述
思路:

这很明显是一道dp题
要求从1到2021的最短距离,就是从1开始,依次求出到2,到3,到4……到2021的最短距离。
例子:
若要求1-4的最短距离,就求出min(1->2)+lcm(2,4),min(1->3)+lcm(3,4),lcm(1,4) 的最小值
若要求1-24的最短距离(24-21=3所以从3开始),就要求出min(1->3)+lcm(3,24),min(1->4)+lcm(4,24)……,min(1->23)+lcm(23,24) 的最短距离
.
可能有人会疑惑1->24的最短距离?不是1只能到22吗?答:当你算出1->24的最短距离后,dp[24]代表的就是1->24的最短距离。dp[23]就是min(1->23)。
参考以下解析加强理解:

在这里插入图片描述

答案:

10266837

#include <iostream>
using namespace std;

int dp[2022];
int gcd(int a, int b) //求出最大公约数 
{
	return b ? gcd(b, a % b) : a;
}
int lcm(int a, int b) //最小公倍数 
{
	return a * b / gcd(a, b);
}
void fun(int n)
{
	int i;
	if (n < 22)
		i = 1;
	else
		i = n - 21;
	int min = dp[i] + lcm(i, n);
	for (++i; i < n; i++)
	{
		int temp = dp[i] + lcm(i, n);
		min = temp > min ? min : temp;
	}
	dp[n] = min;
	return;
}
int main()
{
	dp[1] = 0;
	for (int i = 2; i <= 2021; i++)
	{
		fun(i);
	}
	cout << dp[2021];
}

F 时间显示

在这里插入图片描述
【样例输入1】

46800999

【样例输出1】

13:00:00

【样例输入2】

1618708103123

【样例输出2】

01:08:23

【评测用例规模与约定】
对于所有评测用例,给定的时间为不超过 1018 的正整数。
思路:

1秒等于1000毫秒
将毫秒位直接咔嚓了变成秒,然后进行天数取余,小时取余,分钟数取余
long long 最大1019

答案:

//一天  86400 秒
//一小时 3600 秒
//一分钟 60   秒 
#include<iostream>
#include<stdio.h>
using namespace std;
int main()
{
	long long int n;
	int h,m,s;
	cin >> n;
	n/=1000;//咔嚓毫秒 
	n=n%86400;//天数取余
	h=n/3600;
	n=n%3600;//小时取余
	m=n/60;
	n=n%60;//分钟数取余
	s=n;
	printf("%02d:%02d:%02d",h,m,s);
	return 0;
}

G 砝码称重

在这里插入图片描述
【样例输入】

3
1 4 6

【样例输出】

10

【样例说明】

能称出的 10 种重量是:1、2、3、4、5、6、7、9、10、11。
1 = 1;
2 = 6 − 4 (天平一边放 6,另一边放 4);
3 = 4 − 1;
4 = 4;
5 = 6 − 1;
6 = 6;
7 = 1 + 6;
9 = 4 + 6 − 1;
10 = 4 +6;
11 = 1 + 4 + 6。

【评测用例规模与约定】
对于 50% 的评测用例,1 ≤ N ≤ 15。
对于所有评测用例,1 ≤ N ≤ 100,N 个砝码总重不超过 100000。

思路1(考场思路):

  • 类似于dp
  • 设一个vector容器(与int动态数组同理)
  • map查重
  • 每次拿出一个砝码,与vector容器前面存的依次做加减法,若新得到的数在vector中没有出现,那就放到vector容器中(每次放入vector中都要验重,包括砝码)

批注:显然会超时,如果有更好的方法欢迎在评论区留言
思路2(学习别人的):

讲解地址:https://www.bilibili.com/video/BV1fv411L78t?p=1

跟着y总学,用闫氏dp分析法

在这里插入图片描述

答案1(原始):下面有 答案2

#include<iostream>
#include<vector>
#include<map>
using namespace std;
int ABS(int n)
{
	return n>0?n:(-n);
}
int main()
{
	vector<int>v;
	map<int,int>mp;
	mp[0]=1;
	int N;
	cin >> N;
	long long int ans=0;
	int j=0;
	for(int i=0;i<N;i++)
	{
		int temp;
		cin >> temp;
		if(mp[temp]==0)
		{
			mp[temp]=1;
			v.push_back(temp);
			ans++;
			j=ans;
		}
		else j=ans+1;
		for(int i=0;i<j-1;i++)
		{
			if(mp[ABS(temp-v[i])]==0)
			{
				ans++;
				v.push_back(ABS(temp-v[i]));
				mp[ABS(temp-v[i])]=1;
			}
			if(mp[temp+v[i]]==0)
			{
				ans++;
				v.push_back(temp+v[i]);
				mp[temp+v[i]]=1;
			}
		}
	}
/*	for(int i=0;i<ans;i++)
	{
		cout << v[i]<< " ";
	}
	cout << endl;
*/
	cout << ans;
	return 0;
}

答案2:

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

#define N 110
#define M 200010 
int a[N];
bool dp[N][M];

int main()
{
	int n;
	int m=0;//记录砝码总重 
	cin >> n;
	for(int i=1;i<=n;i++)
	{
		cin >> a[i];
		m+=a[i];
	}
	dp[0][0]=true;
	for(int i=1;i<=n;i++)
		for(int j=0;j<=m;j++)
			dp[i][j]=dp[i-1][j]||dp[i-1][j+a[i]]||dp[i-1][abs(j-a[i])];//三个中有任意1个为true就可以 
	int ans=0;
	for(int j=1;j<=m;j++)
	{
		if(dp[n][j])
		ans++;
	}
	cout << ans;
	return 0;
}

附1:得到100个随机数

#include<stdio.h>
#include<stdlib.h>
#include<time.h>//这里
#define random1(x,y) (rand()%(y-x+1)+x)
int main()
{
	srand( (unsigned)time( NULL ) );//这里
	
	int i;
	for(i=0;i<100;i++)
	{
		printf("%d\n",random1(1,100000));//1~100000之间随机数
	}
	
	return 0;
}

H 杨辉三角

在这里插入图片描述
【样例输入】

6

【样例输出】

13

【评测用例规模与约定】
对于 20% 的评测用例,1 ≤ N ≤ 10;
对于所有评测用例,1 ≤ N ≤ 1000000000。

思路:

  • 每行的每个元素除了第一个和最后一个都是自己上面的左右两个相加得到的结果
  • 利用滚动数组(节省空间),来得到杨辉三角的每个数。

批注:方法过于暴力,如果有更好的方法欢迎在评论区留言

答案:

#include<iostream>
using namespace std;

long long int ans=1;
int *N;

int main()
{
	N=new int[1];
	N[0]=1;
	long long int n ;
	cin >> n;
	if(n==1)
		cout << 1;
	else
	{
		for(int i=2;;i++)
		{
			int *P=new int[i];
			int tool=0;//判断有没有找到这个数,用来结束循环
			for(int j=0;j<i;j++)
			{
				if(j==0||j==i-1)
				{
					P[j]=1;
				}
				else P[j]=N[j-1]+N[j];
				if(P[j]==n)
				{
					ans++;
					tool=1;
					break;
				}
				else ans++;
			}
			if(tool)
				break;
			delete(N);
			N=P;
		}
		cout << ans;
	}
	return 0;
}

I 双向排序

在这里插入图片描述
【样例输入】

3 3
0 3
1 2
0 2

【样例输出】

3 1 2

【样例说明】

原数列为 (1, 2, 3)。
第 1 步后为 (3, 2, 1)。
第 2 步后为 (3, 1, 2)。
第 3 步后为 (3, 1, 2)。与第 2 步操作后相同,因为前两个数已经是降序了。

【评测用例规模与约定】
对于 30% 的评测用例,n, m ≤ 1000;
对于 60% 的评测用例,n, m ≤ 5000;
对于所有评测用例,1 ≤ n, m ≤ 100000,0 ≤ ai ≤ 1,1 ≤ bi ≤ n。

思路:

傻乎乎的用快排

批注:最多只能过前60%的样例,如果有更好的方法欢迎在评论区留言

答案:

#include<iostream>
using namespace std;

int *N;
void Quick1(int i,int j)//升 
{
	if(i>=j)
		return;
	int mid=(i+j)/2;
	int s=N[mid];
	N[mid]=N[i];
	int n=i,m=j;
	while(n<m)
	{
		while(n<m)
		{
			if(N[m]>s)
			{
				m--;
			}
			else
			{
				N[n++]=N[m];
				break;
			}
		}
		while(n<m)
		{
			if(N[n]<s)
			{
				n++;
			}
			else
			{
				N[m--]=N[n];
				break;
			}
		}
	}
	N[m]=s;
	Quick1(i,m-1);
	Quick1(m+1,j);
}
void Quick2(int i,int j)//降
{
	if(i>=j)
		return;
	int mid=(i+j)/2;
	int s=N[mid];
	N[mid]=N[i];
	int n=i,m=j;
	while(n<m)
	{
		while(n<m)
		{
			if(N[m]<s)
			{
				m--;
			}
			else
			{
				N[n++]=N[m];
				break;
			}
		}
		while(n<m)
		{
			if(N[n]>s)
			{
				n++;
			}
			else
			{
				N[m--]=N[n];
				break;
			}
		}
	}
	N[m]=s;
	Quick2(i,m-1);
	Quick2(m+1,j);
}
int main()
{
	int n,m;//n个数 m次操作 
	cin >> n>> m;
	N=new int[n+1];
	for(int i=1;i<=n;i++)
	{
		N[i]=i;
	}
	while(m--)
	{
		int pi,qi;
		cin >> pi >> qi;
		if(pi==0)
		{
			Quick2(1,qi);
		}
		else Quick1(qi,n);
	}
	for(int i=1;i<=n;i++)
	{
		cout << N[i] << " ";
	}
	return 0;
}

J 括号序列(菜批博主不会)

在这里插入图片描述
【样例输入】

((()

【样例输出】

5

【评测用例规模与约定】
对于 40% 的评测用例,|s| ≤ 200。
对于所有评测用例,1 ≤ |s| ≤ 5000。

批注:菜批博主正在努力

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值