蓝桥杯2021年真题

A卡片

简单的% 和/

B直线

在这里插入图片描述
答案 40257

思路:

1、直线两点式方程:(y-y1)/(y1-y2)=(x-x1)/(x1-x2)
整理的AX+BY+C=0
其中:
A=y1-y2
B=x2-x1
C=x1y2-y1x2
记得A B C要化简,即同时除以他们的最大公约数,方便去重。
只要A B C中有一个不同,就是不同的直线
2、去重:
遍历直线LINE结构体数组,如果两个结构体中A,B,C数据相同,那么就认为这两个直线是重合的直线。第一条直线的不能判断它与哪条直线重合,那么把该直线的value数据赋值为1。从第二条直线开始,与它前面的所有直线作比较,如果前面与该直线重合的直线,那么该直线的value数据不变,等于前一个直线的value数值;如果前面没有与该直线重合的直线,那么该直线的value数据将在前一个直线的value数值加1。遍历所有的直线后,最后一条直线的value值就是去重后的直线条数。
注意:最终的直线条数是 最后一条直线的value值 加上 平行于x轴(包含x轴)直线和平行于y轴(包含y轴)直线和。

#include <bits/stdc++.h>
#define row 21 
#define col 20 
#define pointnum 420
using namespace std;
const int maxn=1000000;

struct POINT{//存储所有点的坐标 
	int x;
	int y;
}point[row*col];
struct LINE{//存储所有直线的数据 
	int a;
	int b;
	int c;
	int value;
}line[maxn];
int sum=0;//直线的数量 

int gcd(int x,int y){//求两个数(可能是0 或者 负数)绝对值的 最大公约数
if(x==0) return y;//0的情况 
if(y==0) return x;
if(x<0) x=0-x;//负数的情况 
if(y<0) y=0-y;
  if(x%y==0) return y;//正常情况 
  else if(y%x==0) return x;
  else if(x>y) return gcd(x-y,y);
  else return gcd(y-x,x); 
	
}

int beingline(int x1,int y1,int x2,int y2){//构造直线ABC并且存储value 
//任意两点构成一条直线 
	int a,b,c;
	if(x1==x2) return 0;//除去竖直线 
	if(y1==y2) return 0;//除去水平线 
	if(x1!=x2&&y1!=y2){//其他斜线 
		a=y1-y2;
		b=x2-x1;
		c=x1*y2-y1*x2;
	}
		int fm;//系数ABC 的最大公约数 
		if(c==0) fm=gcd(a,b);
		else fm=gcd(gcd(a,b),c);
		line[sum].a=a/fm;
		line[sum].b=b/fm;
		line[sum].c=c/fm;
		line[sum].value=1;
		sum++;//直线总数 
		return 0;
	
}


int main(){
	int k=0;
	//存入点的数据 
	for(int i=0;i<col;i++){
		for(int j=0;j<row;j++){
			point[k].x=i;
			point[k].y=j;
			k++;
		}
	}
	//存入直线数据 
	for(int i=0;i<pointnum;i++){
		for(int j=i+1;j<pointnum;j++){
			beingline(point[i].x,point[i].y,point[j].x,point[j].y);
			
		}
	}
	//去重过程第i条直线跟0~i-1条直线比 看是否是同一条直线 
	//value 记录当前直线之前不同直线的个数即最后一条直线的value+col+row是答案 
	for(int i=1;i<sum;i++){
		for(int j=0;j<i;j++){
			if(line[i].a==line[j].a&&line[i].b==line[j].b&&line[i].c==line[j].c){
				line[i].value=line[i-1].value;
				break;
			}
			line[i].value=line[i-1].value+1;
		} 
	}
    printf("%d",line[sum-1].value + col + row);//加上水平线和竖直线 
	return 0;
}

C 货物摆放

问题描述

小蓝有一个超大的仓库,可以摆放很多货物。

现在,小蓝有 n 箱货物要摆放在仓库,每箱货物都是规则的正方体。小蓝规定了长、宽、高三个互相垂直的方向,每箱货物的边都必须严格平行于长、宽、高。

小蓝希望所有的货物最终摆成一个大的长方体。即在长、宽、高的方向上分别堆 L、W、H 的货物,满足 n = L ×W×H。

给定 n,请问有多少种堆放货物的方案满足要求。

例如,当 n=4 时,有以下 6 种方案:1×1×41×2×21×4×12×1×22×2×14×1×1。

请问,当 n = 2021041820210418 (注意有 16 位数字)时,总共有多少种方案?

提示:建议使用计算机编程解决问题。

输出:

2430

最开始打算,这种暴力复杂度高,电脑运行不出来。

 #include <iostream>
 using namespace std;
 int main()
 {
   // 请在此输入您的代码
   long long ans=0;
   for(int i=1;i<=2021041820210418;i++){
     for(int j=1;j<=2021041820210418;j++){
       for(int k=1;k<=2021041820210418;k++){
           if(i*j*k==2021041820210418)
           ans++;
       }
     }
   }
   cout<<ans<<endl;
   return 0;
 }

化繁从简是这道题的奥义,降低循环次数(先找n的所有因子,再从因子里面循环)

D路径

【问题描述】
小蓝学习了最短路径之后特别高兴,他定义了一个特别的图,希望找到图中的最短路径。
小蓝的图由 2021 个结点组成,依次编号 1 至 2021。
对于两个不同的结点 a, b,如果 a 和 b 的差的绝对值大于 21,则两个结点之间没有边相连;如果 a 和 b 的差的绝对值小于等于 21,则两个点之间有一条长度为 a 和 b 的最小公倍数的无向边相连。
例如:结点 1 和结点 23 之间没有边相连;结点 3 和结点 24 之间有一条无向边,长度为 24;结点 15 和结点 25 之间有一条无向边,长度为 75。
请计算,结点 1 和结点 2021 之间的最短路径长度是多少。
提示:建议使用计算机编程解决问题。
答案
10266837

思路:

解题思路
首先确定问题:求结点1到结点2021之间的最短路径,又因为知道只要结点相差大于21,则无法有边到达,换句话说,结点值相差大于21则需要借助一个甚至多个结点到达,若结点相差小于等于21,则可以确定最短路径。所以我们可以每个结点轮换遍历求最小路径。比如知道了结点1到结点i的最短路径,可以通过结点i求出结点i到结点(i+21)的所有的最短路径(但不是最后的结果,很可能在结点i+j(1<j<21)的轮换遍历时会更新最短路径
确定状态:
定义状态:f[i]为结点1到结点i的最短路径,初始值为0,代表这个结点1到结点i的最短路径还未更新。
边界值:即结点1到结点1的最短路径为1 or 结点1到结点2的最短路径为2(可直接赋值)

在这里插入图片描述
f[j]=0,代表这个最短路径还未被更新,知道结点i之后,最多可以更新到结点i+21的值,如果到i+22之后则他们无边相连,无法直接更新。

如果f[j]≠0,说明已经更新过f[j]的值了,则继续更新f[j]的值,则需要与之前最后一次更新f[j]的值最比较,取较小着。

代码如下:

#include<iostream>
using namespace std;
int f[2050];//f[i]表示结点1到 结点i的最短路径值 
int lcm(int a,int b){//最小公倍数
	int t;
	int x=a;
	int y=b;
	while(y!=0){
		t=x%y;
		x=y;
		y=t;
	}
	return a*b/x;
//	int maxbound=a*b;//这种方法超时
//	for(int i=a;i<=maxbound;i++){
//		if(i%a==0&&i%b==0){
//			return i;
//			}
//		}
}
int main(){
	for(int i=0;i<2050;i++)		f[i]=0;
	for(int i=1;i<=2021;i++){
		for(int j=i+1;j<=i+21;j++){
			if(f[j]==0)  f[j]=f[i]+lcm(j,i);
			else	f[j]=min(f[j],f[i]+lcm(j,i));
		}
	}
	cout<<f[2021]<<endl;
	return 0;
} 

E回路计数

问题描述   蓝桥学院由 21 2121 栋教学楼组成,教学楼编号 1 11 到 21 2121。对于两栋教学楼 a aa 和 b bb,当 a aa 和 b bb 互质时,a aa 和 b bb 之间有一条走廊直接相连,两个方向皆可通行,否则没有直接连接的走廊。
  小蓝现在在第一栋教学楼,他想要访问每栋教学楼正好一次,最终回到第一栋教学楼(即走一条哈密尔顿回路),请问他有多少种不同的访问方案?两个访问方案不同是指存在某个
i ii,小蓝在两个访问方法中访问完教学楼 i ii 后访问了不同的教学楼。   提示:建议使用计算机编程解决问题。
答案提交   这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。

答案: 881012367360

思路:状态压缩+DP
在这里插入图片描述
代码:

#include<iostream>
using namespace std;
typedef long long ll;
//求 a,b最大公约数,互质数---两个数的最大公约数是1 
int gcd(int a, int b)//6 4 
{
	if(a%b==0) return b;
	else if(b%a==0) return a;
	else if(a>b) return gcd(a-b, b);
	else return gcd(b-a, a);
}
const int n = 21, m = 1 << n;
ll dp[m][n];//dp[i][j]:走到j点时,状态为i时的方案总数 
bool e[n][n];//标记两点是否互质 

int main()
{
	for (int i=1; i<=21;i++)
		for (int j = 1; j <= 21; j ++)
			if(gcd(i,j)==1)
				e[i - 1][j - 1] = true;//互质为true,且把21个点标号为0~20 
				
	dp[1][0] = 1;//初始化:走到第一个点是状态为0001的方案数为1; 
	for (int i = 1; i <= m - 1; i ++) {
	//m=1<<21即22个1的二进制,m-1是21个1的二进制 
		for (int j = 0; j <= 20; j ++){
		
			if(i >> j & 1)//j这栋楼我是不是访问过,访问过才向下走; 
				for (int k = 0; k <= 20; k ++){
				
					if(((i>>k)&1)&&e[k][j])
			//状态转移方程:走到j栋楼状态为i的方案总数=
			//走到j之前的那个状态(i-(1<<j)),对应的最后楼栋是任意一栋满足条件的,k循环;
			//条件是i这个状态已访问过k--(i>>k)&1==1;并且k和j互质,即k能走到j.
						dp[i][j]+= dp[i-(1<<j)][k];
					}
			}  
}
	ll ans = 0;
for (int i=1;i<=20;i++)//楼栋数0~20 ,回到第一栋楼前的最后一栋楼可以是标号1~20的任意一 
  ans+=dp[m-1][i];//状态m-1是21个1的二进制,i是除了第一栋 所有楼栋循环 
	
	cout<<ans<<endl;	
	return 0;								
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值