蓝桥杯算法入门_27 (2016真题)

文章目录

  • 2016
    • 报纸页数 (中学填空题)
    • 煤球数目 (简单题)
    • 平方怪圈 (模拟)
    • 打印方格 (简单题)(先跑代码观察,测试多组数据)
    • 快速排序 (快排模板)
    • 凑算式 (全排列 + 通分 --- *模板*)
    • 寒假作业: (全排列 + check)
    • 冰雹数 (*大数处理* 大数-加法|减法|乘法|除法 -- 模板)
    • 卡片换位 (BFS:从一个状态到达另一个状态的步数)
    • 密码脱落 (还原回文串 -- 朴素解法 - 模拟dfs | 最佳解 : 最长公共子序列 reverse + check )
  • 2016总结
    • 模拟题总结(公约数 - set去重 - 螺旋矩阵模板)

2016

报纸页数 (中学填空题)

X星球日报和我们地球的城市早报是一样的,
都是一些单独的纸张叠在一起而已。每张纸印有4版。
比如,某张报纸包含的4页是:5,6,11,12,
可以确定它应该是最上边的第2张报纸。
我们在太空中捡到了一张X星球的报纸,4个页码分别是:
1125,1126,1727,1728
请你计算这份报纸一共多少页(也就是最大页码,并不是用了几张纸哦)?
请填写表示总页数的数字。
注意:你提交的应该是一个整数,不要填写任何多余的内容或说明性文字。

思路:
1125 * 2 + 同一张纸的差值(1728 - 1125 - 1) = 2250 + 603 - 1 = 2853 - 1 = 2852 (从1728开始到结尾包括1728重复算 ,减1!!!)
还有规律法:1126 - 2 + 1728 …

煤球数目 (简单题)

简单题画图
有一堆煤球,堆成三角棱锥形。具体:
第一层放1个,
第二层3个(排列成三角形),
第三层6个(排列成三角形),
第四层10个(排列成三角形),

如果一共有100层,共有多少个煤球?
请填表示煤球总数目的数字。
注意:你提交的应该是一个整数,不要填写任何多余的内容或说明性文字。

1
1 + 2
1 + 2 + 3
1 + 2 + 3 + 4

ans = 171700

#include 
using namespace std;
int test_01() {
	int ans = 0;
	for(int i = 1; i <= 100; i++) {
		for(int j = i; j > 0; j--) {
			ans += j;
		}
	}

	cout << ans << endl;
	return 0;
}
/*
一层循环 增量
	int pre = 1; //每轮多加
	int plus = 2;//每轮相对增量
	long sum = 1;
	for(int i = 2;i <= 100;i++){
		pre +=(pre + plus);
		sum += pre;
		plus++;
	}

平方怪圈 (模拟)

(想太复杂 --直接输出每次的结果观察 比如先3的循环圈观察最大值 …再4…)
如果把一个正整数的每一位都平方后再求和,得到一个新的正整数。
对新产生的正整数再做同样的处理。
如此一来,你会发现,不管开始取的是什么数字,
最终如果不是落入1,就是落入同一个循环圈。
请写出这个循环圈中最大的那个数字。
请填写该最大数字。
注意:你提交的应该是一个整数,不要填写任何多余的内容或说明性文字。

思路
四位数排除:(92 + 92 + 92 + 92 == 324 < 1000)
枚举范围 [0,324];
尝试 2, 4 ,16 ,1+36 == 37 ,9+49==58 , 25+64 ==89 , 64 + 81 = 145 ,1+16+25 = 42 ,16+4 = 20 , 4 …


/* ~my~~~~~~~~超时
int ans = 0;
bool check(int temp[]) {
	for(int i = 0; i < 50; i++) {
		for(int j = 0; j < 50; j++) {
			if(temp[i] == temp [j]) {
				return true;
			}
		}
	}
	return false;
}
int temp[100];
void dfs(int i,int sum,int cnt) { //sum统计每次累加后的值 --成为新数  ,cnt统计平方循环次数 , i为起始测试值
	temp[0] = i;
	if(check(temp)){
		for(int k = 0;k < 50;k++){
			if(ans < temp[k]){
				ans = temp[k];
			}
		}
	}
	while(temp[cnt]){
		if(i / 10 ) { // i/10 != 0执行
			sum += (i%10) * (i%10) ;
		}
		i /= 10;
	}
	temp[cnt] = sum;//存放每次新的数
	dfs(sum,0,cnt + 1);
}

int test_02() {
	int sum = 0;
	for(int i = 0; i <325; i++) {
		dfs(i,0,0);
	}
	cout << ans << endl;
	return 0;
}
*/
#include
int extract(int start) {
	string str;
	stringstream ss;
	ss << start;
	ss >> str;
	int ans = 0;
	for(int i = 0; i < str.length(); i++) {
		ans += (str[i] - '0')*(str[i] - '0');
	}
	return ans;
}
int test_02() {
	int start = 2; // 2 ,3 , 4 ...    那些不会落到1的数,会到一个循环圈里循环
	int cnt = 0;
	while(cnt < 1000) {
		cout << start <<endl;
		int sum = extract(start);
		start = sum;
		cnt++;
	}
	return 0;
}



打印方格 (简单题)(先跑代码观察,测试多组数据)

		小明想在控制台上输出 m x n 个方格。
	比如 10x4的,输出的样子是:
		+---+---+---+---+---+---+---+---+---+---+
		|   |   |   |   |   |   |   |   |   |   |
		+---+---+---+---+---+---+---+---+---+---+
		|   |   |   |   |   |   |   |   |   |   |
		+---+---+---+---+---+---+---+---+---+---+
		|   |   |   |   |   |   |   |   |   |   |
		+---+---+---+---+---+---+---+---+---+---+
		|   |   |   |   |   |   |   |   |   |   |
		+---+---+---+---+---+---+---+---+---+---+

如果显示有问题,可以参见【图1.jpg】)
以下是小明写的程序,请你分析其流程,填写划线部分缺少的代码。

#include 
//打印m列,n行的方格
void f(int m, int n)
{
int row;
int col;

for(row=0; row<n; row++){
for(col=0; col<m; col++) printf("+---");
printf("+\n");
for(col=0; col<m; col++) printf("|   ");
printf("|\n");
}

printf("+");
_____________________________;   //填空  for(col = 0;col < m;col++)printf("---+");  跑代码观察,更改其他数据测试
printf("\n");
}


int main()
{
f(10,4);
return 0;
}


//注意:仅仅填写划线部分缺少的内容,不要添加任何已有内容或说明性文字。

快速排序 (快排模板)

排序在各种场合经常被用到。
快速排序是十分常用的高效率的算法。
其思想是:先选一个“标尺”,
用它把整个队列过一遍筛子,
以保证:其左边的元素都不大于它,其右边的元素都不小于它。
这样,排序问题就被分割为两个子区间。
再分别对子区间排序就可以了。
下面的代码是一种实现,请分析并填写划线部分缺少的代码。

#include 

void swap(int a[], int i, int j)
{
	int t = a[i];
	a[i] = a[j];
	a[j] = t;
}

int partition(int a[], int p, int r) //定一个标识,小于标识放左边,大于标识放右边 (交换实现)
{
    int i = p;//标识
    int j = r + 1;//右端点
    int x = a[p];//左端点的值
    while(1){
        while(i<r && a[++i]<x);
        while(a[--j]>x);
        if(i>=j) break;
        swap(a,i,j);
    }
	swap(a,p,j); //答案

    return j;
}

void quicksort(int a[], int p, int r)
{
    if(p<r){
        int q = partition(a,p,r);
        quicksort(a,p,q-1);
        quicksort(a,q+1,r);
    }
}

int main()
{
	int i;
	int a[] = {5,13,6,24,2,8,19,27,6,12,1,17};
	int N = 12;

	quicksort(a, 0, N-1);

	for(i=0; i<N; i++) printf("%d ", a[i]);
	printf("\n");

	return 0;
}

注意:只填写缺少的内容,不要书写任何题面已有代码或说明性文字。
*/

凑算式 (全排列 + 通分 — 模板)

   	     B      DEF
	A + --- + ------- = 10
		 C      GHI

(如果显示有问题,可以参见【图1.jpg】)
这个算式中AI代表19的数字,不同的字母代表不同的数字。
比如:
6+8/3+952/714 就是一种解法,
5+3/1+972/486 是另一种解法。
这个算式一共有多少种解法?
注意:你提交应该是个整数,不要填写任何多余的内容或说明性文字。
坑点: 分数通分后相加才为10
A - - - a[0] , B - - - a[1] , C – a[2] , DEF - - - x , GHI - - - y
通分可整除: (分子1*分母2 + 分子2 * 分母1) % (分子 * 分母) == 0


#include //swap(a,i,j)  next_permutation(a,a+length);
int a[] = {1,2,3,4,5,6,7,8,9};
int ans;
bool check() {
	int x = a[3] * 100 + a[4] * 10 + a[5];
	int y = a[6] * 100 + a[7] * 10 + a[8]; //通分后可以整除 (才为整数) + 满足等式 == true
	if((a[1] * y + a[2] * x) % (y * a[2]) == 0 && a[0] + (a[1] * y + a[2] * x) / (y * a[2] ) == 10 ) {
		return true;
	}
	return false;
}
/*递归回溯生成全排列,适用于无重复元素的情况*/
/*考虑第k位,前面的已经排定*/
void f(int k) { //起始 k = 0 ,f(0)
	if(k == 9) { //每种排列 判断
		if(check()) {
			ans++;
		}
	}
	//全排列模板部分
	//从k往后的数字都可以放到k位  i = k
	for(int i = k; i < 9; i++) {
		//{int t = a[i];a[i] = a[k];a[k] = t;} //swap(a[i],a[k]);  可以替换!
		swap(a[i],a[k]);
		f(k + 1);//递归
		swap(a[i],a[k]);
		//{int t = a[i];a[i] = a[k];a[k] = t;} //回溯  swap(a[i],a[k]);
	}
}

int test_03() {
	//法一:
	//已给算法模板全排列
	do {
		if(check()) {
			ans++;
		}
	} while(next_permutation(a,a+9)); //注意此模板限制条件 !!!  数组a必须是有序的

	//f(0);    //法二: 手写全排列
	cout << ans << endl;
	return 0;
}

寒假作业: (全排列 + check)

排列组合问题:用代码解决前,看看如何用数学解题!!! (概率论)
现在小学的数学题目也不是那么好玩的。
看看这个寒假作业:

□ + □ = □
□ – □ = □
□ × □ = □
□ ÷ □ = □

每个方块代表1~13中的某一个数字,但不能重复。
比如:
6 + 7 = 13
9 – 8 = 1
3 * 4 = 12
10 / 2 = 5
以及:
7 + 6 = 13
9 – 8 = 1
3 * 4 = 12
10 / 2 = 5
就算两种解法。(加法,乘法交换律后算不同的方案)
你一共找到了多少种方案?
请填写表示方案数目的整数。
注意:你提交的应该是一个整数,不要填写任何多余的内容或说明性文字。

:多加一个条件 a2[9] % a2[10] == 0 实际除法后为整数 相等时,是指能整除的情况 而不是int舍去后的相等!!!
超时13! = 6.2270208e+9 ~~ 62秒 --仅仅适用于填空题 :64 --解决:提前剪枝
扩展:此题若不能交换律算同一种,若不能重复,用数学思维:先算出重复的所有解 / 每种重复的个数


int a2[] = {1,2,3,4,5,6,7,8,9,10,11,12,13};
int ans2 = 0;
bool check2() {
	if(a2[0] + a2[1] == a2[2] && a2[3] - a2[4] == a2[5] && a2[6] * a2[7] == a2[8] &&
	        a2[9] % a2[10] == 0&&a2[9] / a2[10] == a2[11]) {
		return true;
	}
	return false;
}
void f2(int k) {
	if(k == 13) {
		if(check2()) { //可以在此处(*最后*添加检验)
			//	printf(" %d + %d = %d , %d - %d = %d ,%d * %d = %d \n",a2[0],a2[1],a2[2],a2[3],a2[4],a2[5],a2[6],a2[7],a2[8]);
			ans2++;
		}
	}
	for(int i = k; i < 13; i++) {
		swap(a2[i],a2[k]);

		if(k == 2 && a2[0] + a2[1] != a2[2] || k == 5 && a2[3] - a2[4] != a2[5] ) { //剪枝 提前check排除不可行排列
			swap(a2[i],a2[k]);//消除影响continue的时候,下面一条回退就没有执行 !!!
			continue;
		}

		f2(k + 1);
		swap(a2[i],a2[k]);
	}
}

int test_04() {

	f2(0);
	cout << ans2 << endl;
	return 0;
}

冰雹数 (大数处理 大数-加法|减法|乘法|除法 – 模板)

任意给定一个正整数N,
如果是偶数,执行: N / 2
如果是奇数,执行: N * 3 + 1
生成的新的数字再执行同样的动作,循环往复。
通过观察发现,这个数字会一会儿上升到很高,
一会儿又降落下来。
就这样起起落落的,但最终必会落到“1”
这有点像小冰雹粒子在冰雹云中翻滚增长的样子。
比如N=9
9,28,14,7,22,11,34,17,52,26,13,40,20,10,5,16,8,4,2,1
可以看到,N=9的时候,这个“小冰雹”最高冲到了52这个高度。
输入格式:
一个正整数N(N<1000000)
输出格式:
一个正整数,表示不大于N的数字,经过冰雹数变换过程中,最高冲到了多少。
例如,输入:
10
程序应该输出:
52
再例如,输入:
100
程序应该输出:
9232
资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗 < 1000ms
请严格按要求输出,不要画蛇添足地打印类似:“请您输入…” 的多余内容。
所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。
注意:不要使用package语句。不要使用jdk1.7及以上版本的特性。
注意:主类的名字必须是:Main,否则按无效代码处理。

坑! :即使是 unsign long long 也会溢出 变成负数 无法算出正确答案
测试i是否会溢出 (变负数)
if(i < 0){
cout << i << endl;
}

/* bug~~~~~~~


#include
#include
typedef long long ll;
ll ans3 = -1;   //ans3 : 1654740898      注意边界0  ans3初始应该为 -1    !!!
ll n;
ll func(ll i) { //没有大数处理 , 提交得分 30%
	ll ans3 = - 1;
	while( i != -1) {
		ans3 = max(ans3,i);
		i = (i % 2 == 1) ? i * 3 + 1 : i / 2;

	}
	return ans3;
}
int test_05() {
	scanf("%lld",&n);
	for(ll i = 1; i <= n; i++) {
		ll res = func(i);
		ans3 = max(ans3,res);//注意:max无法处理ll规模会截断
	}
	printf("%lld\n",&ans3);
	return 0;
}



#inculde
string f(ll i){   //大数运算
	string ans = "0";  //字符串
	string i_str;
	stringstream ss;
	ss << i;
	ss >> i_str;

	while(i_str != "1"){
		if(cmp(i_str,ans)==1){  //i_str值更大
			ans = i_str;
		}
		bool isEven1 = isEven(i_str);//判断奇偶 ,偶数返回true
		if(isEven1){//偶数  --除以2
			i_str = divide2(i_str);
		}
		else{  //奇数 -- 乘3加1
			i_str = add1(mul3(i_str));
		}
	}
	return ans;
}

*/

卡片换位 (BFS:从一个状态到达另一个状态的步数)

你玩过华容道的游戏吗?
这是个类似的,但更简单的游戏。
看下面 3 x 2 的格子

+---+---+---+
| A | * | * |
+---+---+---+
| B |   | * |
+---+---+---+

在其中放5张牌,其中A代表关羽,B代表张飞, * 代表士兵。
还有一个格子是空着的。
你可以把一张牌移动到相邻的空格中去(对角不算相邻)。
游戏的目标是:关羽和张飞 交换位置,其它的牌随便在哪里都可以。
输入格式:
输入两行6个字符表示当前的局面
输出格式:
一个整数,表示最少多少步,才能把AB换位(其它牌位置随意)

例如,输入:
* A
**B
程序应该输出:
17

再例如,输入:
A B
***
程序应该输出:
12

思路: BFS +位置标记 + set去重复情况

/*................................................暂时略

void swap(char*s,int i,int j){
	char t = s[i];
	s[i] = s[j];
	s[j] = t;
}
//    char*     先不用...

#include
#include
char *start;
int posA,posB;
pair  posO; //下面全改 ......
struct StateAndlevel {
	char *start;
	int level;
	int pos;
};

void checking(char *state){
	//posA位置是B ,posB的位置是A -- true
	return (state[posA] == 'B' && state[posB] == 'A');
}

void bfs() {
	queue q;
//将初始状态加入队列
	q.push(StateAndlevel(start,0,posO);
	while(!q.empty()) {
	StateAndlevel &front = q.front();

//弹出队首,将队首和目标的状态比较,如果相同,结束,注意压入队列的数据应该记录了层次
		char *start = front.state;
		int le = front.pos;//空格的位置
		if(checking(state)) {
			printf("%d\n",le);
			break;
		}
		q.pop();
//将队首的邻居加入队列,重复上一步,注意,重复状态不要加入  (set) 换行符吃掉!getchar()
		if(x - 1 >= 0){
			char *new_state = (char*)malloc(6*sizeof(char));
			strcpy(new_state,state);
			swap(new_state,x,x-1);
//			查重

		}
	}
}

int test_06() {
	malloc(6 * sizeof(char));//处理输入,起始字符串长度为6
	int index = 0;
	for(int i = 0; i < 2; i++) {
		for(int j = 0; j < 3; j++) { //标记出A,B的位置
			start[index] = getchar();
			if(start[index] == 'A')posA = i * 3 + j;
			if(start[index] == 'B')posA = i * 3 + j;
			if(start[index] == ' ')posO = i * 3 + j;
			index++;
		}
	}
	getchar();
	return 0;
}

密码脱落 (还原回文串 – 朴素解法 - 模拟dfs | 最佳解 : 最长公共子序列 reverse + check )

X星球的考古学家发现了一批古代留下来的密码。
这些密码是由A、B、C、D 四种植物的种子串成的序列。
仔细分析发现,这些密码串当初应该是前后对称的(也就是我们说的镜像串)。
由于年代久远,其中许多种子脱落了,因而可能会失去镜像的特征。
你的任务是:
给定一个现在看到的密码串,计算一下从当初的状态,它要至少脱落多少个种子,才可能会变成现在的样子。
输入一行,表示现在看到的密码串(长度不大于1000)
要求输出一个正整数,表示至少脱落了多少个种子。
例如,输入:
ABCBA
则程序应该输出:
0
再例如,输入:
ABDCDCBABC
则程序应该输出:
3
资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗 < 3000ms
请严格按要求输出,不要画蛇添足地打印类似:“请您输入…” 的多余内容。
所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。

思路
添加字符位置:从两边补,判断找最少添加次数) 不同就加 dfs取最小
真正解法: ans == 字符串总长度 - 字符串正序和逆序的最长公共子序列
(dp模板 )
if(s1[i-1]==s2[j-1])
dp[i][j]=dp[i-1][j-1]+1;
else
dp[i][j]=max(dp[i-1][j],dp[i][j-1]);

#include
char s[1000];

int dfs(char s[],int left,int right,int cnt) {  // 时间 2^len/2^
	if(left >= right) { //递归出口
		return cnt;
	}
	if(s[left] != s[right]) { //不等一种是左边添加 ,另一种是右边添加
		return min(dfs(s,left + 1,right,cnt+1),dfs(s,left,right - 1,cnt+1)); //选择最终次数少的那种
	} else { //两端相同
		return dfs(s,left+1,right-1,cnt);
	}
}

int test_06() {
	int ans = 0;
	scanf("%s",s);
	int len = strlen(s);//减少时间
	ans = dfs(s,0,len-1,0);
	printf("%d\n",ans);
	return 0;
}
/*最长公共子序列法【dp模板】: */
#include
#include
#include
#include
#include
#include
using namespace std;
int main() {
	string s1,s2;
	cin>>s1;
	int len=s1.length();
	s2=s1;
	reverse(s1.begin(),s1.end());
	//二维数组解法
	int dp[len+1][len+1];
	memset(dp,0,sizeof(dp));
	for(int i=1; i<=len; i++) {
		for(int j=1; j<=len; j++) {
			if(s1[i-1]==s2[j-1])
				dp[i][j]=dp[i-1][j-1]+1;
			else
				dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
		}
	}
	//一维数组解法
	int dp2[len+1];
	memset(dp2,0,sizeof(dp2));
	for(int i=1; i<=len; i++) {
		for(int j=1; j<=len; j++) {
			if(s1[i-1]==s2[j-1])
				dp2[j]=dp2[j-1]+1;
			else
				dp2[j]=max(dp2[j-1],dp2[j]);
		}
	}
	printf("%d\n",len-dp[len][len]);
	printf("%d\n",len-dp2[len]);
	return 0;
}

2016总结

01 报纸页数 (中学填空题)
02 煤球数量 (简单规律)
03 平方怪圈 (模拟 - 数据范围选取 - 填空肉眼观察数据)
04 打印方格 (简单题)(先跑代码观察,测试多组数据)
05 快速排序 (熟悉快排 - 填空题)
06 凑算式 (全排列 + 通分 — 模板)
07 寒假作业 (全排列 + check)
08 冰雹数 (大数处理 -typedef long long ll; - 大数乘法 - 模板
09 卡片交换 (华容道互换位置 - BFS +位置标记 + set去重复情况 )
10 密码脱落 (朴素解法 - 模拟dfs | 最佳解 : 最长公共子序列 reverse + check )

int main() {

	test_06();

	return 0;
}

模拟题总结(公约数 - set去重 - 螺旋矩阵模板)

模拟题:
1.三个数公约数
求出每个数的约数存放在set数组中,自动去重 ,然后判断两个set都存在的数为公约数存到set ans数组中 ,答案为 ans.size()
for 0 - n if(n%i == 0) set1.insert(i)
2.螺旋矩阵:
可以Excel打表
1 2 3 先右拉,再竖着两个数下拉
螺旋矩阵(n行m列):模板 n*n (思路 : 往内填,每层边界不断-1)

//引用模板~
int l = 0, r = n - 1, t = 0, b = n - 1; //左,右,上,下   
int mat[n][n];//可以开全局 
int num = 1, max = n * n;
while(num <= max){
	  for(int i = l; i <= r; i++) mat[t][i] = num++; // left to right.
	   t++;//行数 
	   for(int i = t; i <= b; i++) mat[i][r] = num++; // top to bottom.
	   r--;//右边界 
	   for(int i = r; i >= l; i--) mat[b][i] = num++; // right to left.
	   b--;//下边界 
	   for(int i = b; i >= t; i--) mat[i][l] = num++; // bottom to top.
	   l++;
}

你可能感兴趣的:(算法,蓝桥杯,c++)