蓝桥杯专题之找规律篇

题目列表:

2013年:黄金连分数,翻硬币

2014年:切面条

2015年:移动距离

2016年:煤球数目

2018年:螺旋折线

2020年:蛇形填数,平面切分,子串分值和

2022年模拟赛:螺旋矩阵

1.黄金连分数

黄金分割数0.61803… 是个无理数,这个常数十分重要,在许多工程问题中会出现。有时需要把这个数字求得很精确。 
对于某些精密工程,常数的精度很重要。也许你听说过哈勃太空望远镜,它首次升空后就发现了一处人工加工错误,对那样一个庞然大物,其实只是镜面加工时有比头发丝还细许多倍的一处错误而已,却使它成了“近视眼”!! 
言归正传,我们如何求得黄金分割数的尽可能精确的值呢?有许多方法。 
比较简单的一种是用连分数:

              1
黄金数 = ---------------------
                    1
         1 + -----------------
                      1
             1 + -------------
                        1
                 1 + ---------
                      1 + ...
这个连分数计算的“层数”越多,它的值越接近黄金分割数。 
请你利用这一特性,求出黄金分割数的足够精确值,要求四舍五入到小数点后100位。 
小数点后3位的值为:0.618 
小数点后4位的值为:0.6180 
小数点后5位的值为:0.61803 
小数点后7位的值为:0.6180340 
(注意尾部的0,不能忽略) 
你的任务是:写出精确到小数点后100位精度的黄金分割值。 
注意:尾数的四舍五入! 尾数是0也要保留! 
显然答案是一个小数,其小数点后有100位数字,请通过浏览器直接提交该数字。 

答案:

0.6180339887498948482045868343656381177203091798057628621354486227052604628189024497072072041893911375

代码:

import java.math.BigDecimal;

public class Main {
    public static void main(String[] args) {
       BigDecimal a = new BigDecimal(1);
       BigDecimal b = new BigDecimal(2);
       for(int i = 0;i < 500;i++){
           BigDecimal temp = a;
           a = b;
           b = b.add(temp);
       }
       System.out.println(a.divide(b,100,BigDecimal.ROUND_HALF_DOWN));
    }
}

2.翻硬币

问题描述
小明正在玩一个“翻硬币”的游戏。

桌上放着排成一排的若干硬币。我们用 * 表示正面,用 o 表示反面(是小写字母,不是零)。

比如,可能情形是:**oo***oooo

如果同时翻转左边的两个硬币,则变为:oooo***oooo

现在小明的问题是:如果已知了初始状态和要达到的目标状态,每次只能同时翻转相邻的两个硬币,那么对特定的局面,最少要翻动多少次呢?

我们约定:把翻动相邻的两个硬币叫做一步操作,那么要求:

输入格式
两行等长的字符串,分别表示初始状态和要达到的目标状态。每行的长度<1000

输出格式
一个整数,表示最小操作步数。

样例输入1
**********
o****o****
样例输出1
5
样例输入2
*o**o***o***
*o***o**o***
样例输出2
1

分析:

遍历字符串,依次寻找与目标字符串第一个不同的位置和第二个不同的位置,如果找到一对,就继续往后寻找

ans = sum(第二个不同的位置-第一个不同的位置)

代码:

#include<iostream>
using namespace std;
int main(){
	string src,s;
	cin >> src;
	cin >> s;
	int first,second;
	bool flag = true;
	int ans = 0;
	for(int i = 0;i < s.length();i++){
		if(s[i] != src[i]){
			if(flag){
				first = i;
				flag = false;
			}else{
				second = i;
				ans += second-first;
				flag = true;
			}
		}
	}
	cout << ans;
	return 0;
}

3.切面条

一根高筋拉面,中间切一刀,可以得到2根面条。
如果先对折1次,中间切一刀,可以得到3根面条。
如果连续对折2次,中间切一刀,可以得到5根面条。
那么,连续对折10次,中间切一刀,会得到多少面条呢?

答案:1025

分析:

0       2

1       3

2       5

规律:

f(n) = 2*f(n-1) - 1

代码:

#include<iostream>
using namespace std;
int main(){
	int a[11] = {0};
	a[0] = 2;
	a[1] = 3;
	for(int i = 2;i <= 10;i++){
		a[i] = 2*a[i-1] - 1;
	}
	cout << a[10];
	return 0;
}

4.移动距离

题目描述 

X星球居民小区的楼房全是一样的,并且按矩阵样式排列。

其楼房的编号为 1,2,3…

当排满一行时,从下一行相邻的楼往反方向排号。

比如:当小区排号宽度为 6 时,开始情形如下:

1 2 3 4 5 6
12 11 10 9 8 7
13 14 15 …

我们的问题是:已知了两个楼号 m和 n,需要求出它们之间的最短移动距离(不能斜线方向移动)。

输入格式

输入共一行,包含三个整数 w,m,n,w 为排号宽度,m,n为待计算的楼号。

输出格式

输出一个整数,表示 m,n两楼间最短移动距离。

数据范围

1≤w,m,n≤10000

输入样例:

6 8 2

输出样例:

4

分析:

m和n所对应的行号就是(n-1)/w,(m-1)/w;所对应的列号需要分奇偶性,如果行号为偶数,那么列号就是(n-1)%w,如果行号是奇数,那么列号就是w-1-(n-1)%w

两栋楼房之间的最短距离就是横坐标的差值+纵坐标的差值

代码:

#include<iostream>
#include<algorithm>
using namespace std;
int w,m,n;
struct P{
	int x,y;
};
P getSex(int n){
	P p;
	p.x = (n-1)/w;
	if(p.x % 2 == 0){
		p.y = (n-1)%w;
	}else{
		p.y = w-1-(n-1)%w;
	}
	return p;
}
int main(){
	cin >> w >> m >> n;
	P p1 = getSex(m);
	P p2 = getSex(n);
	cout << abs(p1.x-p2.x) + abs(p1.y-p2.y);
	return 0;
}

5.煤球数目

有一堆煤球,堆成三角棱锥形。
具体: 第一层放1个,
第二层3个(排列成三角形),
第三层6个(排列成三角形),
第四层10个(排列成三角形), …
如果一共有100层,共有多少个煤球?
请填表示煤球总数目的数字。

答案:171700

分析:

1     1

2     3

3     6

4     10

我们会发现1+2=3;3+3=6;6+4=10

代码:

#include<iostream>
using namespace std;
int main(){
	int sum = 0,n = 0;
	for(int i = 1;i <= 100;i++){
		n += i;
		sum += n;
	}
	cout << sum;
	return 0;
}

6.螺旋折线

如图p1.png所示的螺旋折线经过平面上所有整点恰好一次。  

对于整点(X, Y),我们定义它到原点的距离dis(X, Y)是从原点到(X, Y)的螺旋折线段的长度。  



例如dis(0, 1)=3, dis(-2, -1)=9  

给出整点坐标(X, Y),你能计算出dis(X, Y)吗?

【输入格式】
X和Y  

对于40%的数据,-1000 <= X, Y <= 1000  
对于70%的数据,-100000 <= X, Y <= 100000  
对于100%的数据, -1000000000 <= X, Y <= 1000000000  

【输出格式】
输出dis(X, Y)  

【样例输入】
0 1

【样例输出】
3

分析:

将所有的边拉直,让这个图形成为一个迭代的小正方形

将(1,1),(2,2),(3,3)这样的点作为中间的媒介,正方形四条边上的点都可以根据它求出

例如:

(1,1) : 4   

上边和左边(0,1):3 (-1,1):2   (-1,0):1       

可以发现:4 - [(1-0)+(1-1)] = 3;4 - [(1-(-1))+(1-1)] = 2……

右边和下边  (1,0):5    (1,-1):6    (0,-1):7

可以发现:4 + [(1-1)+(1-0)] = 5;4 + [(1-1)+(1-(-1))] = 6……ta

代码:

#include<iostream>
#include<cmath>
using namespace std;
int main(){
	long long x,y;
	cin >> x >> y;
	long long n = max(abs(x),abs(y));
	long long base = 4*n*n;
	long long bx = n,by = n;
	if(x == bx || y == -by){//正方形右边和下边
		cout << base + (abs(bx-x) + abs(by-y));
	}else{//正方形上边和左边
		cout << base - (abs(bx-x) + abs(by-y));
	}
	return 0;
}

7.蛇形填数

如下图所示,小明用从 1 开始的正整数 “蛇形” 填充无限大的矩阵
在这里插入图片描述

容易看出矩阵第二行第二列中的数是 5,请你计算矩阵中第 20 行第 20 列的数是多少? 

答案:761

分析: 

我们只观察对角线上的元素:

(1,1):1    (2,2):5    (3,3):13……

(1,1)在斜行的第0行;(2,2)在斜行的第2行;(3,3)在斜行的第4行……(n,n)在斜行的第2*(n-1)行

所以,(1,1):1        (2,2):1+2+2        (3,3):1+2+3+4+3

(20,20) : \frac{38*(1+38)}{2} + 20 = 761

8.平面切分

【问题描述】
平面上有 N 条直线,其中第 i 条直线是 y = Ai · x + Bi。
请计算这些直线将平面分成了几个部分。
【输入格式】
第一行包含一个整数 N。
以下 N 行,每行包含两个整数 Ai; Bi。
【输出格式】
一个整数代表答案。
【样例输入】
3
1 1
2 2
3 3
【样例输出】
6
【评测用例规模与约定】
对于 50% 的评测用例, 1 ≤ N ≤ 4, −10 ≤ Ai; Bi ≤ 10。
对于所有评测用例, 1 ≤ N ≤ 1000, −100000 ≤ Ai; Bi ≤ 100000。

分析:

规律:第一条直线会产生2个平面,增加一条之前没加入过的直线,平面数+1,如果这条新加的直线与之前加入的直线产生k个不相同的交点,则平面数再+k

注意:

他有可能会给重复数据,这样就会产生重边,加入一条重边,我们就给他打上标记,当他不存在就好了

另外,注意数据类型的设定,平面数可能会超出int的范围,所以面对的数据量较大时建议使用long long类型;当进行数据运算时,如果参与运算的数的数据类型不一致要进行强制转换

代码: 

#include<iostream>
#include<set>
using namespace std;
const int MAX_N = 1010;
int L[MAX_N][2];//存储A,B 

int main(){
	int N;
	cin >> N;
	long long ans = 1; 
	for(int i = 0;i < N;i++){
		cin >> L[i][0] >> L[i][1];
		bool flag = false; 
		for(int j = 0;j < i;j++){
			if((L[j][0] == L[i][0]) && (L[j][1] == L[i][1])){
				flag = true;
				break;
			}
		}
		if(!flag){//若不是重边,再判断交点数
			set<pair<long double,long double> > points;//记录每一条边加进来后与已有直线相交的不同位置的点 
			for(int k = 0;k < i;k++){
				if(L[k][0] != L[i][0]){
					long double x = (long double)(L[i][1]-L[k][1])/(L[k][0]-L[i][0]);
					long double y = (long double)(L[i][0]*L[k][1]-L[k][0]*L[i][1])/(L[i][0]-L[k][0]);
					points.insert(make_pair(x,y));//set自动去重 
				}
				
			}
			ans += points.size()+1;
		}
	} 
	cout << ans;
	return 0;
}

9.子串分值和 

问题描述
对于一个字符串S ,我们定义 S 的分值 f(S) 为S 中出现的不同的字符个数。例如 f(“aba”)=2,f(“abc”)=3, f(“aaa”)=1。

现在给定一个字符串 S[0…n-1](长度为n ),请你计算对于所有S 的非空子串S[i…j] ,(0<=i<=j<=n-1)的和是多少。

输入格式
输入一行包含一个由小写字母组成的字符串 。

输出格式
输出一个整数表示答案。

样例输入

ababc
样例输出

28
样例说明
子串 f值

a     1
ab    2
aba   2
abab  2
ababc 3
 b    1
 ba   2
 bab  2
 babc 3
  a   1
  ab  2
  abc 3
   b  1
   bc 2
    c 1

评测用例规模与约定(n为正整数)
对于 20% 的评测用例:
n<=10
对于 40%的评测用例:
n<=100
对于 60%的评测用例:
n<=1000
对于 80%的评测用例:
n<=10000
对于所有评测用例:
n<=100000

分析:

有时候一个例子我们是发现不了规律的,所以不妨多举几个

做到这种题我就会往贡献度方面想,这个字母贡献了最终答案的多少啊……

这里我就举一个例子(大家可以自己在底下多模拟几个):

下标                1       2       3       4       5

                        a       b      a       b       c

贡献度             5       8      6       4       5

我们发现:     5*1    4*2  3*2    2*2   1*5

                  5*(1-0)    4*(2-0)    3*(3-1)    2*(4-2)    1*(5-0)

规律(在写代码的时候下标从0开始):

        每个位置的字母的贡献度= (字母总个数-该字母下标)*(该字母下标-该字母上一次出现的位置下标)

我们将a,b,c三个字母的初始下标都设为-1

代码:

#include<iostream>
#include<cstring>
#include<map>
using namespace std;
int main(){
	string s;
	cin >> s;
	map<char,int> last;
	for(char i = 'a';i <= 'z';i++){
		last[i] = -1;
	}
	long long ans = 0;
	int n = s.length();
	for(int i = 0;i < n;i++){
		ans += (long long)(n-i)*(i-last[s[i]]);//这里的运算结果可能超过int的范围,需要强制转换为long long类型 
		last[s[i]] = i;
	}
	cout << ans;
	return 0;
}

 
 

10.螺旋矩阵

【问题描述】

对于一个n 行 m 列的表格,我们可以使用螺旋的方式给表格依次填上正整数,我们称填好的表格为一个螺旋矩阵。
例如,一个 4 行 5 列的螺旋矩阵如下:
1 2 3 4 5
14 15 16 17 6
13 20 19 18 7
12 11 10 9 8

【输入格式】
第一行包含两个整数 n, m,分别表示螺旋矩阵的行数和列数。
第二行包含两个整数 r, c,表示要求的行号和列号。

【输出格式】
输出一个整数,表示螺旋矩阵中第 r 行第 c 列的元素的值。

【样例输入】

4 5

2 2

【样例输出】

15

【评测用例规模与约定】
对于 30% 的评测用例,2 ≤ n, m ≤ 20
对于 70% 的评测用例,2 ≤ n, m ≤ 100
对于所有评测用例,2 ≤ n, m ≤ 1000,1 ≤ r ≤ n,1 ≤ c ≤ m

分析:

最重要的就是定界,上下左右四个角作为转折点

代码:

#include <iostream>
using namespace std;
int a[1010][1010];

int main(){
    int n, m, x, y;
	cin >> n >> m;
	cin >> x >> y;
	int num = 1;
    int l = 0, r = m - 1, u = 0, d = n - 1;
	while(num <= n * m){//将所有的数都填进数组 
		for (int i = l; i <= r; i++){//向右 
		    a[u][i] = num++;
		}
		u++;
		for (int i = u; i <= d; i++){//向下 
			a[i][r] = num++;
		} 
		r--;
		for (int i = r; i >= l; i--){//向左 
			a[d][i] = num++;
		}
		d--;
		for(int i = d;i >= u; i--){//向上 
			a[i][l] = num++;
		}
		l++;
	}
	cout << a[x - 1][y - 1];
	return 0;
}

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值