算法渣渣的蓝桥杯笔记

蓝桥杯题目技巧笔记

2013年题目笔记

1.字符串使用技巧

题目标题: 排它平方数
203879 * 203879 = 41566646641
203879 是个6位数,并且它的每个数位上的数字都是不同的,并且它平方后的所有数位上都不出现组成它自身的数字。具有这样特点的6位数还有一个,请你找出它!

如果需要在数字1xx中不能含有数字2x中有的数字!
办法:
将两个数字x和xx分别转成字符串
使用length()函数
s_xx.find(s_x[i])!=string::npos
//s_x[i]在s.x有出现的意思

bool check(long long x, long long xx){
	string s_x,s_xx;
	i2s(x, s_x);
	i2s(xx, s_xx);
	for(int i=0; i<s_x.length(); ++i){
		if(s_xx.find(s_x[i])!=string::nps){
			return false;
		}
	}
	return true;
} 
void i2s(long long x, string &basic_string){
	stringstream ss;
	ss << x;
	ss >> basic_string;
}

题目答案

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

void i2s(long long x, string &basic_string){
	stringstream ss;
	ss << x;
	ss >> basic_string;
}
bool check(long long x, long long xx){
	string s_x,s_xx;
	i2s(x, s_x);
	i2s(xx, s_xx);
	for(int i=0; i<s_x.length(); ++i){
		if(s_xx.find(s_x[i])!=string::npos){
			return false;
		}
	}
	return true;
} 


int main(){
	for(int i=1; i<10; ++i){
		for(int j=0; j<10; ++j){
			if(j!=i)
				for(int k=0; k<10; ++k){
					if(k!=i&&k!=j)
						for(int l=0;l<10;++l)
						{
							if(l!=i&&l!=j&&l!=k)
								for(int m=0; m<10; ++m){
									if(m!=i&&m!=j&&m!=k&&m!=l)
										for(int n=0; n<10; ++n){
											if(n!=i&&n!=j&&n!=k&&n!=l&&n!=m){
												long long x=i*100000+j*10000+k*1000+l*100+m*10+n;
													if(check(x,x*x)){
														cout<<x<<' '<<x*x<<endl;
													}
														
											}
												
									}
								}
						}
						 
				}
		}
			
			
	}
	return 0;
}


2.递归的思想

题目
跳格子
从我做起振
我做起振兴
做起振兴中
起振兴中华
比赛时,先站在左上角的写着“从”字的格子里,可以横向或纵向跳到相邻的格子里,但不能跳到对角的格子或其它位置。一直要跳到“华”字结束。
要求跳过的路线刚好构成“从我做起振兴中华”这句话。
请你帮助小明算一算他一共有多少种可能的跳跃路线呢?

思路想法
枚举所有的走法,枚举有两种方法:

  1. 迭代
  2. 递归

递归的思想
三要素

  1. 找重复,我一开始面临的选择和我后面面临的选择是一样的 想到最简单的情况,就是一个格子或者是两个格子的时候,就只有一种走法
  2. 找变化 x和y坐标在改变
  3. 找出口 什么时候结束
#include <iostream>
using namespace std;

int f(int x,int y){
	if ( x == 3 || y == 4 )
		return 1;//找出口	
	return f( x + 1 ,y ) + f(x ,y + 1);//向左走 找重复 
		 
}
int main(){
	cout<<f(0,0)<<endl; 
	return 0;
}

模拟法

题目标题: 高斯日记
日记日期是一个整数,比如:4210
表示那一天是高斯出生后的第几天。
高斯出生于:1777年4月30日。
5343,那天是:1791年12月15日。
高斯获得博士学位的那天日记上标着:8113
请你算出高斯获得博士学位的年月日。
提交答案的格式是:yyyy-mm-dd, 例如:1980-03-21
答案: 1799-07-16

用编程解决问题 模仿翻日历的方法

#include <iostream>
using namespace std;

bool isleapYear(int y)
{
	return ((y%4==0&&y%100!=0)||y%400==0); 
}

int main(){
	int y = 1777;
	int m=4;
	int d=30;
	for (int i=0;i<8112;++i)
		{
			d++;
			if(m==12&&d==32)
				{
					y++;
					m=1;
					d=1;
					continue;
				}
			if((m==1||m==3||m==5||m==7||m==8||m==10)&&d==32){
				m++;
				d=1;
				continue;
			}
			if((m==4||m==6||m==9||m==11)&&d==31){
				m++;
				d=1;
				continue;
			}
			if((m==2&&isleapYear(y))&&d==30){
				m++;
				d=1;
				continue;
			}
			if((m==2&&!isleapYear(y))&&d==29){
				m++;
				d=1;
				continue;
			}
		}
		cout<<y<<' '<<m<<' '<<d<<endl;
	return 0;
}


储存多个数据的方法

颠倒的价牌
小李的店里卖样品电视机,其标价都是4位数字
这种价牌有个特点,对一些数字,倒过来看也是合理的数字。如:1 2 5 6 8 9 0 都可以。
牌子挂倒了,有可能完全变成了另一个价格,比如:1958 倒着挂就是:8561,差了几千元
多数情况不能倒读。
两个挂倒的价格牌的电视机卖出去
一个价牌赔了2百多,另一个价牌却赚了8百多,综合起来,反而多赚了558元
问:赔钱的那个价牌正确的价格应该是多少?

解题思路
//枚举所有四位数中可以颠倒的
//将其颠倒过来,与原来的数值做差,将-200多和+800的记录下来,分别记录在两个集合中
//遍历两个集合将-+两两求和,结果为558的为正确答案

#include<iostream>
#include<sstream>
#include<vector>
using namespace std;

void i2s(int num,string &str)
{
	stringstream ss;
	ss<<num;
	ss>>str;
}
void s2i(string &str,int &num)
{
	stringstream ss;
	ss<<str;
	ss>>num;
}
char to(char x)
{
	if(x=='6')return '9';
	else if(x=='9') return '6';
	else return x; 
}
string reverse(const string &str)
{
	string ans;
	for(int i=3;i>=0;i--)
	{
		ans.insert(ans.end(),to(str[i]));
	}
	return ans;
}
struct price
{
	int a,b,c;//原始价格 错误价格 差价 
};
vector<price> v1;//存储-200多的 
vector<price> v2;//存储+800多的 
int main()
{
//	cout<<reverse("1958")<<endl;
	for(int i=1000;i<10000;i++)
	{
		string str;
		i2s(i,str);
		if(str.find('3')!=string::npos||str.find('4')!=string::npos||str.find('7')!=string::npos||str.find('0')==3)
			continue;
		string r = reverse(str);
		int r_int;
		s2i(r,r_int);//r_int 是翻转后的价格,i是原始价格 
		int plus=r_int-i; 
		if(plus>-300&&plus<-200)
		{
			price p ={i,r_int,plus};
			v1.push_back(p);
		} 
		if(plus>800&&plus<900)
		{
			price p ={i,r_int,plus};
			v2.push_back(p);
		} 
		
		for(int i=0;i<v1.size();i++)
			for(int j=0;j<v2.size();j++)
				if(v1[i].c+v2[j].c==558)
				{
					cout<<v1[i].a<<" "<<v1[i].b<<" "<<v1[i].c<<" "<<endl;
					cout<<v2[j].a<<" "<<v2[j].b<<" "<<v2[j].c<<" "<<endl;
					cout<<v1[i].c+v2[j].c<<endl;
				}
	}
}

答案为9088

前缀判断

题目标题:前缀判断

如下的代码判断 needle_start指向的串是否为haystack_start指向的串的前缀,如不是,则返回NULL。

比如:“abcd1234” 就包含了 “abc” 为前缀


char* prefix(char* haystack_start, char* needle_start)
{
char* haystack = haystack_start;
char* needle = needle_start;


while(*haystack && *needle){
if(______________________________) return NULL; //填空位置
}

if(*needle) return NULL;

return haystack_start;
}
#include<iostream>
#include<sstream>
using namespace std;

char* prefix(char* haystack_start, char* needle_start)
{
	char* haystack = haystack_start;//定义一个指针指向母串 
	char* needle = needle_start;//定义一个指针指向前缀 


	// *+指针就是取内容 
	//意思是两个指针都没有越界   
	while(*haystack && *needle){
		
		//母串和子串有不匹配的地方直接返回NULL 
		if(*(haystack++)!=*(needle++)) return NULL; //填空位置
	}
	
	//如果needle没有越界 说明字串更长 所以也返回NULL 
	if(*needle) return NULL;
	
	//如果needle越界了 直接说明匹配成功 返回haystack 
	return haystack_start;
} 

int main()
{
	cout<<prefix("abcd123","abc")<<endl;
	return 0;
}

波兰表达式

正常的表达式称为中缀表达式,运算符在中间,主要是给人阅读的,机器求解并不方便。

例如:3 + 5 * (2 + 6) - 1

而且,常常需要用括号来改变运算次序。

相反,如果使用逆波兰表达式(前缀表达式)表示,上面的算式则表示为:

    • 3 * 5 + 2 6 1

不再需要括号,机器可以用递归的方法很方便地求解。

为了简便,我们假设:

  1. 只有 + - * 三种运算符
  2. 每个运算数都是一个小于10的非负整数

下面的程序对一个逆波兰表示串进行求值。
其返回值为一个结构:其中第一元素表示求值结果,第二个元素表示它已解析的字符数。

struct EV
{
int result; //计算结果
int n; //消耗掉的字符数
};

struct EV evaluate(char* x)
{
struct EV ev = {0,0};
struct EV v1;
struct EV v2;

if(*x==0) return ev;

if(x[0]>=‘0’ && x[0]<=‘9’){
ev.result = x[0]-‘0’;
ev.n = 1;
return ev;
}

v1 = evaluate(x+1);
v2 = _____________________________; //填空位置

if(x[0]’+’) ev.result = v1.result + v2.result;
if(x[0]
’*’) ev.result = v1.result * v2.result;
if(x[0]==’-’) ev.result = v1.result - v2.result;
ev.n = 1+v1.n+v2.n;

return ev;
}

请分析代码逻辑,并推测划线处的代码,通过网页提交。
注意:仅把缺少的代码作为答案,千万不要填写多余的代码、符号或说明文字!!

 1 #include <iostream>
 2 using namespace std;
 3 
 4 struct EV
 5 {
 6     int result;  //计算结果 
 7     int n;       //消耗掉的字符数 
 8 };
 9 
10 struct EV evaluate(char* x)
11 {
12     struct EV ev = { 0, 0 };
13     struct EV v1;
14     struct EV v2;
15 
16     if (*x == 0) return ev;
17 
18     if (x[0] >= '0' && x[0] <= '9'){
19         ev.result = x[0] - '0';
20         ev.n = 1;
21         return ev;
22     }
23 
24     v1 = evaluate(x + 1);
25     v2 = evaluate(x + 1 + v1.n);  //填空位置//因为x+1+1不对 在v1下面继续去吃字符 然后就v1.n没有用上 后面用到了v1.result.....
26 
27     if (x[0] == '+') ev.result = v1.result + v2.result;
28     if (x[0] == '*') ev.result = v1.result * v2.result;
29     if (x[0] == '-') ev.result = v1.result - v2.result;
30     ev.n = 1 + v1.n + v2.n;
31 
32     return ev;
33 }
34 
35 int main()
36 {
37     char *x = "-+3*5+261";//!!重重点
38     EV ev = evaluate(x);
39     cout << ev.result << "\t" << ev.n << endl;
40 
41     return 0;
42 }

错误票据

假设断号不可能发生在最大和最小号。
要求程序首先输入一个整数N(N<100)表示后面数据行数。
接着读入N行数据。
每行数据长度不等,是用空格分开的若干个(不大于100个)正整数(不大于100000)
每个整数代表一个ID号。
要求程序输出1行,含两个整数m n,用空格分隔。
其中,m表示断号ID,n表示重号ID
例如:
用户输入:
2
5 6 8 11 9
10 12 9
则程序输出:
7 9

再例如:
用户输入:
6
164 178 108 109 180 155 141 159 104 182 179 118 137 184 115 124 125 129 168 196
172 189 127 107 112 192 103 131 133 169 158
128 102 110 148 139 157 140 195 197
185 152 135 106 123 173 122 136 174 191 145 116 151 143 175 120 161 134 162 190
149 138 142 146 199 126 165 156 153 193 144 166 170 121 171 132 101 194 187 188
113 130 176 154 177 120 117 150 114 183 186 181 100 163 160 167 147 198 111 119

则程序输出:
105 120

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

const int MaxN=10000;
int line;
int data[MaxN]; 

void s2i(string &str,int &num)
{
	stringstream ss;
	ss<<str;
	ss>>num; 
} 

int main()
{
	scanf("%d",&line);//接受行数作为一个数字 
	getchar();//吃掉换行符 
	int index=0;
	for (int i=0;i<line;++i)
	{
		string s;//定义一个string 
		/*重点代码部分 分割字符串!!*/
		getline(cin,s);//从标准输入流当中输入到字符串当中		
		istringstream iss(s);//将s封装到istringstream当中 
		string tmp;
		// getline有分割的功能 但是输入流必须是iss 
		//每次切割放在tmp当中 
		while(getline(iss,tmp,' ')) 
		{
			s2i(tmp,data[index++]);
		}
		/*重点代码部分 分割字符串!!*/
	}
	//cout<<index<<endl;
	
	sort(data,data + index);//sort(数组名,数组名+数组长度)
	//sort按照升序排列 
	int a,b;
	
	for(int i=1;i<index;i++)
	{
		if(data[i]==data[i-1]+2)
		{
			a=data[i]-1;
		}
		if(data[i]==data[i-1])
		{
			b=data[i];
		}
	}
	printf("%d %d",a,b);
	
	return 0;
}

买不到的数目

小明开了一家糖果店。他别出心裁:把水果糖包成4颗一包和7颗一包的两种。糖果不能拆包卖。
小朋友来买糖的时候,他就用这两种包装来组合。当然有些糖果数目是无法组合出来的,比如要买 10 颗糖。
你可以用计算机测试一下,在这种包装情况下,最大不能买到的数量是17。大于17的任何数字都可以用4和7组合出来。
本题的要求就是在已知两个包装的数量时,求最大不能组合出的数字。
输入:
两个正整数,表示每种包装中糖的颗数(都不多于1000)
要求输出:
一个正整数,表示最大不能买到的糖数
不需要考虑无解的情况
例如:
用户输入:
4 7
程序应该输出:
17
再例如:
用户输入:
3 5
程序应该输出:
7

在这里插入图片描述

在这里插入代码片

快速幂运算

//快速幂运算
long pow_2(int b)
{
	long x=2;
	long res=1;
	while(b>0)
	{
		if(b>0)
			res=res*x;
		b>>=1;
		x=x*x;
	}
	return res;
} 

最大公约数

//求最大公约数 
int gcd(long a,long b)
{
	return b==0?a:gcd(b,a%b);
	
	
//	if(b==0) return a;
//	return gcd(b,a%b);
}

关于include在这里插入图片描述

#include<iostream>
#include<set>
using namespace std;
typedef long long LL;
const LL MAX=59084709587505;

int main()
{
	/*
	LL ans=0;
	for(int i=3;i<=MAX,i++)
	{
		int x=i;
		while(x%3==0)x/=3;
		while(x%5==0)x/=5;
		while(x%7==0)x/=7;
		if(x==1)
			ans++;
	}
	cout<<ans<<endl;*/
	int a[3]={3,5,7};
	LL tou=1;
	set<LL> s;
	while(true)
	{
		for(int i=0;i<3;i++)
		{
			LL tt=tou*a[i];
			if(tt<=MAX)
			{
				s.insert(tt);
			}
		}
		//从set中选择比tou大的最小的数 
		tou=*(s.upper_bound(tou));
		if(tou>=MAX)break;
	}
	cout<<s.size()<<endl;
	return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值