蓝桥杯大赛决赛整理合集(B组C/C++)

蓝桥杯大赛决赛整理合集(B组C/C++)

根据大纲梳理一遍,也在全文最后补充了最几年的决赛真题,全文基于C++编写,希望对你有所帮助
关于省赛的反思:
1.我的Code::Block 20.03在机房的电脑调不出来(我决定还是用DEV)
2.暴力算法枚举不要指望学校机房的CPU
3.注意数据的规模,写完一部分代码块记得调试运行一下
ps:真题做下来不难发现小明是个究极强迫症QVQ,期末没有什么时间写,还要英语四级,提前cue借口,但还是加油!!
——以上是2021年的整理
经过寒假对考研数据结构的复习,对算法有了新的感悟) 。
继续整理的栈 队列 树 图 排序

大纲要求

C/C++程序设计基础:包含使用 C/C++编写程序的能力。该部分不考查选手对某一语法的理解程度,选手可以使用自己喜欢的语句编写程序。选手可在 C 语言程序中使用标准 C 的库函数,在 C++语言程序中使用标准 C++的库函数(包括 C 库、STL 等)。
计算机算法:枚举、排序、搜索、计数、贪心、动态规划、图论、数论、博弈论*、概率论*、计算几何*、字符串算法等。
数据结构:数组、对象/结构、字符串、队列、栈、树、图、堆、平衡树/线段树、复杂数据结构*、嵌套数据结构*等。

一、STL

1.vector容器(单端数组)

vector容器可以动态地扩展(原数据拷贝新空间)

#include<iostream>
#include<vector>
using namespace std;
int main(){
	vector<int> v1;//默认构造
	v1.push_back(1);//尾部插入 
	v1.pop_back();//尾部删除最后一个元素
	v1.insert(v1.begin(),100);//在迭代器指定位置插入数据
	v1.erase(v1.begin());//删除迭代器指定位置
	v1.clear();//清空 
	cout<<v1.capacity()<<endl;//输出容量	 
	cout<<v1.size()<<endl;//元素个数 
	v1.resize(15,100);//指定默认填充值拓展容量
	cout<<v1[0]<<" "<<v1[1]<<endl; //输出数据
	vector<int> v2(v1.begin(),v1.end());//区间方式构造
	vector<int> v3(10,100);//v3中有10个100
	vector<int> v4(v3);//拷贝构造
	if(v1.empty()){//判断是否为空 
		cout<<"空"<<endl;
	}
	v1.reserve(100);//预留空间,元素不可以访问 
	v1.swap(v2);//交换两个容器中的数据 
	vector<int>(v).swap(v)//巧用可以压缩空间
	return 0;
} 

2.queue容器(队列——先进先出)

一般会结合bfs使用

#include<iostream>
#include<queue>
using namespace std;
int main(){
	queue<int> q;
	q.push(1);//入队
	q.push(2); 
	cout<<"队列是否为空:"<<q.empty()<<endl;
	cout<<"队头元素是:"<<q.front()<<endl;
	cout<<"队尾元素是:"<<q.back()<<endl;
	q.pop();//出队 
	cout<<"队列元素个数:"<<q.size()<<endl;
	return 0; 
} 

3.map/multimap容器(关联式——二叉树)

可以很方便地用Key值查找value值
map容器:不允许有重复key值
multimap容器:允许有重复key值

#include<iostream>
#include<map>
using namespace std;
class cmp{//指定降序插入 
	public:
		bool operator()(int v1,int v2){
			return v1>v2;
		}
}; 
int main(){
	map<int,int> m1;//创建map容器 
	map<int,int> m2(m1);//拷贝构造 
	map<int,int,cmp> m3;//按照指定规则排序 (默认按照key的升序插入)
	m1.insert(pair<int,int>(2,30));//插入数据 
	m1.insert(make_pair(1,10));//插入数据(这种方式不用写数据类型)
	m1.insert(make_pair(1,20));//不允许插入重复的key值(保留先插入的) 
	cout<<"容器是否为空:"<<m1.empty()<<endl;
	cout<<"容器元素个数:"<<m1.size()<<endl;
	cout<<"容器中有几个key为1的值:" <<m1.count(1)<<endl;
	map<int,int>::iterator pos = m1.find(2);//查找有没有key为2的元素,返回迭代器
	if(pos!=m1.end()){
		cout<<"找到了"<<endl;
	} 
	m1.swap(m2);//交换两个容器的元素 
	cout<<m2[1]<<endl;//利用key访问value值 
	m2.erase(m2.begin());//删除迭代器指定元素
	m2.erase(2);//根据指定的key值删除 
	m2.erase(m2.begin(),m2.end());//根据指定区间删除
	m2.clear();//清空容器 
	return 0; 
} 

二、枚举

三、排序

试题 历届试题 字串排序

问题描述

小蓝最近学习了一些排序算法,其中冒泡排序让他印象深刻。 在冒泡排序中,每次只能交换相邻的两个元素。 小蓝发现,如果对一个字符串中的字符排序,只允许交换相邻的两个字符,则在所有可能的排序方案中,冒泡排序的总交换次数是最少的。 例如,对于字符串 lan 排序,只需要 1次交换。对于字符串 qiao 排序,总共需要4次交换。 小蓝的幸运数字是V,他想找到一个只包含小写英文字母的字符串,对这个串中的字符进行冒泡排序,正好需要V次交换。请帮助小蓝找一个这样的字符串。如果可能找到多个,请告诉小蓝最短的那个。如果最短的仍然有多个,请告诉小蓝字典序最小的那个。请注意字符串中可以包含相同的字符。
输入格式
输入一行包含一个整数 ,为小蓝的幸运数字。
输出格式
输出一个字符串,为所求的答案。
样例输入
4
样例输出
bbaa

四、搜索

一个很赞的BFS和DFS讲解

1.BFS(广度优先——队列)

引子:

试题1 历届试题 九宫重排

问题描述
  如下面第一个图的九宫格中,放着 1~8 的数字卡片,还有一个格子空着。与空格子相邻的格子中的卡片可以移动到空格中。经过若干次移动,可以形成第二个图所示的局面。
在这里插入图片描述
我们把第一个图的局面记为:12345678.把第二个图的局面记为:123.46758 显然是按从上到下,从左到右的顺序记录数字,空格记为句点。本题目的任务是已知九宫的初态和终态,求最少经过多少步的移动可以到达。如果无论多少步都无法到达,则输出-1。
输入格式
  输入第一行包含九宫的初态,第二行包含九宫的终态。
输出格式
  输出最少的步数,如果不存在方案,则输出-1。
样例输入
12345678.
123.46758
样例输出 3
样例输入
13524678.
46758123.
样例输出 22

代码梳理

1.将九宫格的初始状态和目标状态以二维数组的形式存储,并标记初始空白格的位置
2.从初始状态的空白格开始广度优先遍历
->终止条件,达到目标状态,输出步骤/不存在方案 输出-1
->搜索:将当前状态入队,从队头开始,把空白格向四个方向挪动,如果可以挪动,判断是否满足目标九宫格,不满足再把当前状态入队。
  当无法再挪动时,在从队头寻找新的状态,直到队空。
3.剪枝:其中在bfs时,如果遇到之前已经出现的状态就不需要再遍历了。

实现代码

#include<iostream>
#include<bits/stdc++.h>
#include<map>
#include<queue>
using namespace std;
char start[4][4];
char goal[4][4];
int df[4][2]={{-1,0},{1,0},{0,-1},{0,1}};
map<string,int> vis;
struct Node{//结点结构体,存储当前坐标,步骤,九宫格状态 
	int x,y;
	long long step;
	char Map[4][4];
};
bool check_g(Node a){
	for(int i=1;i<=3;i++){
		for(int j=1;j<=3;j++){
			if(a.Map[i][j]!=goal[i][j])
			return false;//如果有一个位置不符合就返回false 
		}
	}
	return true;//全部符合就返回true 
}
bool check_cf(Node a){//判断剪枝 
	string s="";
	for(int i=1;i<=3;i++){
		for(int j=1;j<=3;j++){
			s+=a.Map[i][j];
		}
	}
	if(vis[s]>0)
	return false;
	vis[s]++;
	return true;
}
int bfs(int x1,int y1){//传入空白格的标记坐标 
	Node cur,next;//创立两个结点 (当前和下一个) 
	cur.x=x1;// 传入当前空白格的坐标 
	cur.y=y1;
	cur.step=0;//初始化步骤数 
	for(int i=1;i<=3;i++){
		for(int j=1;j<=3;j++){
			cur.Map[i][j]=start[i][j];//对当前九宫格初始化 
		}
	}
	queue<Node> Q;//创建一个队列 
	Q.push(cur);//将当前结点传入队列 
	if(check_g(cur)){//检查是否已经转化成目标九宫格 
		return cur.step;//已经是目标九宫格返回所需要的步骤 
	}
	while(!Q.empty()){//如果队列不为空 
		cur=Q.front();//取出队头元素 
		Q.pop();//出队 
		for(int i=0;i<4;i++){
			next.x=cur.x+df[i][0];//分别将空白格向四个方向移动 
			next.y=cur.y+df[i][1];
			for(int i1=1;i1<=3;i1++){
				for(int j=1;j<=3;j++){
					next.Map[i1][j]=cur.Map[i1][j];//存入下一个九宫格的状态			
			}
		}
		next.step=cur.step+1;//挪动的步骤+1 
		if(next.x>=1&&next.x<=3&&next.y>=1&&next.y<=3){//如果在边界内 
		swap(next.Map[next.x][next.y],next.Map[cur.x][cur.y]);//移动空白格 
		if(check_cf(next)){//检查是否重复 
			if(check_g(next)){//检查是否满足需求 
			return next.step;
			}
			Q.push(next);//把当前结点入队 
		}
	}
}
	}
	return -1;
}
int main(){
	int x1,y1;//空白格的位置 
	for(int i=1;i<=3;i++){
		for(int j=1;j<=3;j++){
			cin>>start[i][j];//输入起始九宫格状态 
			if(start[i][j]=='.'){
				x1=i; //遇到 。标记位置 
				y1=j;
			}
		}
	}
	for(int i=1;i<=3;i++){
		for(int j=1;j<=3;j++){
			cin>>goal[i][j];//输入目标九宫格的状态 
		}
	}
	cout<<bfs(x1,y1)<<endl;//bfs寻找最小步骤 
	return 0;
} 

在这里插入图片描述

2.DFS(深度优先——栈)

引子:棋盘中的棋子摆放方法有几种

试题1 历届试题 大臣的旅费

问题描述
很久以前,T王国空前繁荣。为了更好地管理国家,王国修建了大量的快速路,用于连接首都和王国内的各大城市。为节省经费,T国的大臣们经过思考,制定了一套优秀的修建方案,使得任何一个大城市都能从首都直接或者通过其他大城市间接到达。同时,如果不重复经过大城市,从首都到达每个大城市的方案都是唯一的。J是T国重要大臣,他巡查于各大城市之间,体察民情。所以,从一个城市马不停蹄地到另一个城市成了J最常做的事情。他有一个钱袋,用于存放往来城市间的路费。聪明的J发现,如果不在某个城市停下来修整,在连续行进过程中,他所花的路费与他已走过的距离有关,在走第x千米到第x+1千米这一千米中(x是整数),他花费的路费是x+10这么多。也就是说走1千米花费11,走2千米要花费23。J大臣想知道:他从某一个城市出发,中间不休息,到达另一个城市,所有可能花费的路费中最多是多少呢?
输入格式
输入的第一行包含一个整数n,表示包括首都在内的T王国的城市数城市从1开始依次编号,1号城市为首都。接下来n-1行,描述T国的高速路(T国的高速路一定是n-1条)每行三个整数Pi, Qi, Di,表示城市Pi和城市Qi之间有一条高速路,长度为Di千米。
输出格式
输出一个整数,表示大臣J最多花费的路费是多少。

#include<iostream>
#include<vector>
#include<bits/stdc++.h>
using namespace std;
struct road{
	int d,l;
};
vector<road> G[10000];
int visit[10000];
int totallen,maxlen,start;
void dfs(int s){
	if(maxlen<totallen){
		maxlen=totallen;
		start=s;
	}
	for(int i=0;i<G[s].size();i++){
		road k=G[s][i];
		if(!visit[k.d]&&k.l){
			visit[k.d]=1;
			totallen+=k.l;
			dfs(k.d);
			visit[k.d]=0;
			totallen-=k.l;
		}
	}
	return;
}
int main(){
	int n,a,s;
	cin>>n;
	road r;
	for(int i=0;i<n-1;i++){
		cin>>s>>r.d>>r.l;
		G[s].push_back(r);
		a=r.d;
		r.d=s;
		G[a].push_back(r); 
	}
	memset(visit,0,sizeof(visit));
	totallen=0;
	visit[1]=1;
	dfs(1);
	memset(visit,0,sizeof(visit));
	totallen=0;
	maxlen=-1;
	visit[start]=1;
	dfs(start);
	cout<<((11+10+maxlen)*maxlen)/2<<endl;
	return 0;
} 

在这里插入图片描述

3.回溯算法总结

一个很好的回溯算法总结

题型:组合,切割,子集,排列,棋盘

回溯模板

void backtracking(参数) {
    if (终止条件) {
        存放结果;
        return;
    }

    for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
        处理节点;
        backtracking(路径,选择列表); // 递归
        回溯,撤销处理结果
    }
}

(1)组合

#include<bits/stdc++.h>
#include<vector>
using namespace std;
vector<int> path;
vector< vector<int> > result;//这里要注意<>中的空格不然会当>>报错
void backtracking(int n,int k,int starIndex){
	if(path.size()==k){
		result.push_back(path);
		for(int i=0;i<path.size();i++){
			cout<<path[i];
		}
		cout<<endl;
		return;
	}
	for(int i=starIndex;i<=n-(k-path.size())+1;i++){//优化剪枝
		path.push_back(i);
		backtracking(n,k,i+1);
		path.pop_back();
	}
}
int main(){
	int n,k;//返回 1 ... n 中所有可能的 k 个数的组合
	cin>>n>>k;
	backtracking(n,k,1); 
	return 0;
}

(2)组合总和

#include<bits/stdc++.h>
#include<vector>
using namespace std;
vector<int> path;
vector< vector<int> > result;
int targetsum;
void backtracking(int n,int k,int sum,int starIndex){
	if(sum>targetsum){
		return;
	}
	if(path.size()==k){
		if(sum==targetsum){
			result.push_back(path);
			for(int i=0;i<path.size();i++){
				cout<<path[i];
			}
			cout<<endl;	
		}	
		return;
	}
	for(int i=starIndex;i<=n;i++){
		sum+=i;
		path.push_back(i);
		backtracking(n,k,sum,i+1);
		sum-=i; 
		path.pop_back();
	}
}
int main(){
	int n,k;//返回 1 ... n 中所有可能的 k 个数的组合使其达到目标值 
	cin>>n>>k>>targetsum;
	backtracking(n,k,0,1);
	cout<<"总共有:"<<result.size()<<"取法"<<endl; 
	return 0;
}

(3)分割回文串

#include<bits/stdc++.h>
#include<vector>
using namespace std;
vector<string> path;
vector< vector<string> > result;
bool isPalindrome(string s,int start,int end){
	for(int i=start,j=end;i<j;i++,j--){
		if(s[i]!=s[j]){
			return false;
		}
	}
	return true;
}
void backtracking(string s,int startIndex){
	if(startIndex>=s.size()){
		result.push_back(path);
		return;
	}
	for(int i=startIndex;i<=s.size();i++){
		if(isPalindrome(s,startIndex,i)){
			string str=s.substr(startIndex,i-startIndex+1);//获取分割子串 
			path.push_back(str);
		}
		else{
			continue;
		}
		backtracking(s,i+1);
		path.pop_back();
	}
}
int main(){
	string s;
	cin>>s;
	backtracking(s,0);
	cout<<"总共有:"<<result.size()<<"取法"<<endl; 
	return 0;
}

(4)求子集

#include<bits/stdc++.h>
#include<vector>
using namespace std;
vector<int> path;
vector< vector<int> > result;
vector<int> nums;
void backtracking(vector<int> nums,int startIndex){
	result.push_back(path);
	for(int i=0;i<path.size();i++){
		cout<<path[i];
	}
	cout<<endl;
	if(startIndex>=nums.size()){
		return;
	}
	for(int i=startIndex;i<nums.size();i++){
		path.push_back(nums[i]);
		backtracking(nums,i+1);
		path.pop_back();
	}
}
int main(){
	int n,num;
	cin>>n;//输入要求子集的集合 
	for(int i=1;i<=n;i++){
		cin>>num;
		nums.push_back(num);
	}
	backtracking(nums,0);
	cout<<"总共有:"<<result.size()<<"子集"<<endl; 
	return 0;
}

五、计数

六、贪心

七、动态规划(本质递归)

引子:Fibonacci sequence
利用递推公式:Fib(n+2) = Fib(n+1) + Fib(n),Fib(1) = Fib(2) = 1
在这里插入图片描述
利用递归的时间复杂度O(2^n),想象成一棵二叉树,每次计算一个新的Fib(n)就要展开一次分支,效率很低。
但是如果把得出结果的Fib(n)、Fib(n-1)保存,Fib(n+1)用到的时候直接调用,是不是就把效率提高了。这就是动态规划的思想。下面利用动态规划解决一下Fibonacci sequence问题。

#include <iostream>
#define MAX 10000
using namespace std;
int Fib(int n){
    int F[MAX];//用数组存储结果
    F[1]=F[2]=1;
    for(int i=3;i<=n;i++)
        F[i]=F[i-1]+F[i-2];
    return F[n];//直接调用
}
int main()
{
    cout<<Fib(6)<<endl;
    return 0;
}

使用情形
1、选择/不选择,求最优解(2维dp)以下试题按照难以程度升序排列

试题1 算法提高 01背包

问题描述
  给定N个物品,每个物品有一个重量W和一个价值V.你有一个能装M重量的背包.问怎么装使得所装价值最大.每个物品只有一个.
输入格式
  输入的第一行包含两个整数n, m,分别表示物品的个数和背包能装重量。
  以后N行每行两个数Wi和Vi,表示物品的重量和价值
输出格式
  输出1行,包含一个整数,表示最大价值。
样例输入
3 5
2 3
3 5
4 7
样例输出
8
数据规模和约定
1<=N<=200,M<=5000.

#include<bits/stdc++.h>
using namespace std;
int W[5004];
int Vi[5004];
int dp[5004][5004];//动态规划 
int main(){
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>W[i]>>Vi[i];
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			dp[i][j]=dp[i-1][j];
			if(j>=W[i]){
				dp[i][j]=max(dp[i][j],dp[i-1][j-W[i]]+Vi[i]);// 
			}
		}
	}
	cout<<dp[n][m];//最大价值 
	return 0;
}

在这里插入图片描述

试题2 试题 算法提高 秘密行动

问题描述
  小D接到一项任务,要求他爬到一座n层大厦的顶端与神秘人物会面。这座大厦有一个神奇的特点,每层的高度都不一样,同时,小D也拥有一项特殊能力,可以一次向上跳跃一层或两层,但是这项能力无法连续使用。已知向上1高度消耗的时间为1,跳跃不消耗时间。由于事态紧急,小D想知道他最少需要多少时间到达顶层。
输入格式
  第一行包含一个整数n,代表楼的高度。
  接下来n行每行一个整数ai,代表i层的楼层高度(ai <= 100)。
输出格式
  输出1行,包含一个整数,表示所需的最短时间。
样例输入
5
3
5
1
8
4
样例输出
1
数据规模和约定
  对20%的数据,n<=10
  对40%的数据,n<=100
  对60%的数据,n<=5000
  对100%的数据,n<=10000

#include<bits/stdc++.h>
using namespace std;
int main(){
	int n;
	cin>>n;
	int ai[10005];
	for(int i=1;i<=n;i++){
		cin>>ai[i];
	}
	int dp[10005][2];//动态规划二维dp[每层楼][每层楼的选择0爬,1跳] 
	memset(dp,0,sizeof(dp));
	dp[1][0]=ai[1];//初始化,第一层选择爬的情况
	dp[1][1]=0;//第一层选择跳的情况
	for(int i=2;i<=n;i++){
		dp[i][0]=min(dp[i-1][0],dp[i-1][1])+ai[i];//如果选择爬的话,那么之前一层可以是爬,也可以是跳上来的
		dp[i][1]=min(dp[i-1][0],dp[i-2][0]);//如果是使用超能力,那么之前的一层/两层必须是爬上来的 
	} 
	cout<<min(dp[n][0],dp[n][1])<<endl;//选择最终耗时最短的方法 
	return 0;
} 

在这里插入图片描述

试题3 算法提高 第二点五个不高兴的小明

问题描述   有一条长为n的走廊,小明站在走廊的一端,每次可以跳过不超过p格,每格都有一个权值wi。   小明要从一端跳到另一端,不能回跳,正好跳t次,请问他跳过的方格的权值和最大是多少?
输入格式   输入的第一行包含两个整数n, p, t,表示走廊的长度,小明每次跳跃的最长距离和小明跳的次数。   接下来n个整数,表示走廊每个位置的权值。
输出格式   输出一个整数。表示小明跳过的方格的权值和的最大值。
样例输入 8 5 3 3 4 -1 -100 1 8 7 6
样例输出 12
数据规模和约定   1<=n, p, t<=1000, -1000<=wi<=1000。

#include<bits/stdc++.h>
using namespace std;
const long long int inf=0x3f3f3f;
int main(){
	long long int n,p,t;
	cin>>n>>p>>t;
	long long int dp[1005][1005]; //当前格,当前跳的最大权值和
	long long int wi[1005];
	for(int i=1;i<=n;i++){//输入每一格距离的权值 
		cin>>wi[i];
	} 
	if(n>p*t){
		n=p*t;//划定距离,使其在跳跃能力范围内 
	}                      
	for(int i=n;i>=n-p&&i>=0;i--){//动态规划的初始化,我们从最后开始,定义最后一步。 
		dp[i][1]=wi[i];//最后一格的最后一跳 
	} 
	for(int i=n;i>=0;i--){
		for(int j=2;j<=t;j++){
			dp[i][j]=-inf;
			for(int k=1;k<=p&&i+k<=n;k++){
				dp[i][j]=max(dp[i+k][j-1],dp[i][j]);
			}
			if(dp[i][j]!=-inf){
				dp[i][j]+=wi[i];//如果找出步数内的最大值,那么加上自身的权值 
			}
		}
	} 
	cout<<dp[0][t]<<endl;
	return 0;
} 

在这里插入图片描述

试题4 算法提高 和谐宿舍2

问题描述
  我的某室友学过素描,墙上有n张他的作品。这些作品都是宽度为1,高度不定的矩形,从左到右排成一排,且底边在同一水平线上。宿舍评比就要来了,为了及格,我们决定买不多于m块的矩形木板,把这些作品和谐掉。要求木板也从左到右排成一排,且底边与作品的底边在同一水平线上。在能够把所有作品和谐掉的前提下,我们希望这些木板的面积和最小,问最小面积和。
  在这里插入图片描述
输入格式
  第一行两个数n和m,表示作品数和木板数;
  第二行n个数Hi,表示从左到右第i个作品的高度。
输出格式
  一行一个数ans,表示答案。
样例输入
5 2
4 2 3 5 4
样例输出
22
数据规模和约定
  对于30%的数据:1<=n,m<=10;
  对于100%的数据:1<=n,m<=100,1<=Hi<=10000。

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
int main(){
	int h[105];
	int hmax[105][105];
	int dp[105][105];
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>h[i];
	} 
	for(int i=1;i<=n;i++){ 
		for(int j=i;j<=n;j++){
			hmax[i][j]=max(hmax[i][j-1],h[j]);
		}
	}
	memset(dp,INF,sizeof(dp));
	for(int i=1;i<=n;i++){ 
		dp[i][1]=i*hmax[1][i];
		for(int j=2;j<=m;j++){
			for(int k=1;k<=i-j+1;k++){
				dp[i][j]=min(dp[i][j],dp[i-k][j-1]+k*hmax[i-k+1][i]);
			}
		}
	}
	cout<<dp[n][m]<<endl;
	return 0;
} 

在这里插入图片描述

八、图论

dfs:

  1. tarjan算法
  2. lca算法

九、数论

十、博弈论

十一、概率论

十二、计算几何

十三、字符串算法

KMP(主要应用在字符串匹配上)

一个很好的KMP总结

前缀:不包含尾字母的所有子串
后缀:不包含首字母的所有子串
前缀表:相等前后缀的记录
在这里插入图片描述

匹配模型

构建前缀表

void getNext_1(int *next,string s){
	int j=0;
	next[j]=0;
	for(int i=1;i<s.size();i++){
		while(j>0&&s[i]!=s[j]){
			j=next[j-1];
		}
		if(s[i]==s[j]){
			j++;
		}
		next[i]=j;
	}
}

主函数调用

int main(){
	string s;//需要匹配的字符
	string s1;//被匹配的字符 
	cin>>s>>s1; 
	int next[s.size()];
	getNext(next,s);
	int j=0;
	for(int i=0;i<s1.size();i++){
		while(j>0&&s1[i]!=s[j]){
			j=next[j-1];
		}
		if(s1[i]==s[j]){
			j++;
		}
		if(j==s.size()){
			cout<<s1<<"可以匹配"<<endl;
			break;
		}
	}
	return 0;	
}

试题1:算法提高 字符串匹配

问题描述
  给出一个字符串和多行文字,在这些文字中找到字符串出现的那些行。你的程序还需支持大小写敏感选项:当选项打开时,表示同一个字母的大写和小写看作不同的字符;当选项关闭时,表示同一个字母的大写和小写看作相同的字符。
输入格式   
  输入的第一行包含一个字符串S,由大小写英文字母组成。
   第二行包含一个数字,表示大小写敏感的选项,当数字为0时表示大小写不敏感,当数字为1时表示大小写敏感。
  第三行包含一个整数n,表示给出的文字的行数。   
  接下来n行,每行包含一个字符串,字符串由大小写英文字母组成,不含空格和其他字符。
输出格式   
  输出多行,每行包含一个字符串,按出现的顺序依次给出那些包含了字符串S的行。
样例输入
Hello 1 5 HelloWorld
HiHiHelloHiHi
GrepIsAGreatTool HELLO
HELLOisNOTHello
样例输出
HelloWorld
HiHiHelloHiHi
HELLOisNOTHello
样例说明
  在上面的样例中,第四个字符串虽然也是Hello,但是大小写不正确。如果将输入的第二行改为0,则第四个字符串应该输出。 评测用例规模与约定
  1<=n<=100,每个字符串的长度不超过100。

#include<bits/stdc++.h>
using namespace std;
string s1[104];
string s;
void getNext_0(int *next,string s){
	int j=0;
	next[j]=0;
	for(int i=1;i<s.size();i++){
		char c1,c2;
		c1=s[i]-'A'+'a';
		c2=s[i]-'a'+'A';
		while(j>0&&c1!=s[j]&&c2!=s[j]&&s[i]!=s[j]){
			j=next[j-1];
		}
		if(s[i]==s[j]||c1==s[j]||c2==s[j]){
			j++;
		}
		next[i]=j;
	}
}
void getNext_1(int *next,string s){
	int j=0;
	next[j]=0;
	for(int i=1;i<s.size();i++){
		while(j>0&&s[i]!=s[j]){
			j=next[j-1];
		}
		if(s[i]==s[j]){
			j++;
		}
		next[i]=j;
	}
}
int main(){
	int num,n;
	cin>>s>>n>>num;
	for(int i=0;i<num;i++){
		cin>>s1[i]; 
	}
	if(s.size()==0){
		return 0;
	}
	int next[s.size()];
	for(int k=0;k<num;k++){
		string p=s1[k];
		if(p.size()<s.size()){
			continue;
		}
		if(n==0){//大小写不敏感 
			getNext_0(next,s);
			int j=0;
			for(int i=0;i<p.size();i++){
				char c1,c2;
				c1=p[i]-'A'+'a';
				c2=p[i]-'a'+'A';
				while(j>0&&p[i]!=s[j]&&c1!=s[j]&&c2!=s[j]){
					j=next[j-1];
				}
				if(p[i]==s[j]||c1==s[j]||c2==s[j]){
					j++;
				}
				if(j==s.size()){
					cout<<p<<endl;
					break;//这里加break很重要不然如果一个字符串中有其他匹配的就会一直输出。
				}
			}
		}
		if(n==1){//大小写敏感 
			getNext_1(next,s);
			int j=0;
			for(int i=0;i<p.size();i++){
				while(j>0&&p[i]!=s[j]){
					j=next[j-1];
				}
				if(p[i]==s[j]){
					j++;
				}
				if(j==s.size()){
					cout<<p<<endl;
					break;
				}
			}
		}
	}
	return 0;	
}

在这里插入图片描述

十四、数组

十五、对象/结构

十六、字符串

十七、队列

试题 算法提高 队列操作

【问题描述】
  队列操作题。根据输入的操作命令,操作队列(1)入队、(2)出队并输出、(3)计算队中元素个数并输出。
输入格式
  第一行一个数字N。
  下面N行,每行第一个数字为操作命令(1)入队、(2)出队并输出、(3)计算队中元素个数并输出。
输出格式
  若干行每行显示一个2或3命令的输出结果。注意:2.出队命令可能会出现空队出队(下溢),请输出“no”,并退出。
样例输入
7
1 19
1 56
2
3
2
3
2
样例输出
19
1
56
0
no
数据规模和约定
  1<=N<=50

【思路】这里用到了queue(队列的STL容器)

定义一个queue的变量     queue<Type> Q
查看是否为空范例        Q.empty()    是的话返回1,不是返回0;
从已有元素后面增加元素   Q.push()
输出现有元素的个数      Q.size()
显示第一个元素          Q.front()
显示最后一个元素        Q.back()
清除第一个元素          Q.pop()

【注意】这里有个很顶的地方就是输出0后退出,就是漏了这个条件一直不能满分,这种编程题一定要看全!!!!

#include<bits/stdc++.h>
#include<queue>
using namespace std;
queue<string> q;
int main(){
	stringstream ss;
	string s1;
	int N,op;
	string num;
	int k=0;
	cin>>N;
	string a[N];
	for(int i=0;i<N;i++){
		cin>>op;
		if(op==1){
			cin>>num;
			q.push(num);
		}
		if(op==2){
			if(!q.empty()){
				a[k++]=q.front();
				q.pop();
			}
			else{
			a[k++]="no";//请输出“no”,并退出。
			break;
			}
			
		}
		if(op==3){
			ss<<q.size();
			ss>>s1; 
			a[k++]=s1;
			ss.clear();
		}
	}
	for(int i=0;i<k;i++){
		cout<<a[i]<<endl;
	}
	return 0;
} 

在这里插入图片描述

十八、栈

试题 算法训练 栈的研究

[问题描述]
宁宁考虑的是这样一个问题:一个操作数序列,从1,2,一直到n(图示为1到3的情况),栈A的深度大于n。
  现在可以进行两种操作,
  1.将一个数,从操作数序列的头端移到栈的头端(对应数据结构栈的push操作)
  2. 将一个数,从栈的头端移到输出序列的尾端(对应数据结构栈的pop操作)
  使用这两种操作,由一个操作数序列就可以得到一系列的输出序列,下图所示为由
  1 2 3
  生成序列
  2 3 1
  的过程。
  在这里插入图片描述
(原始状态如上图所示)
你的程序将对给定的n,计算并输出由操作数序列1,2,…,n经过操作可能得到的输出序列的总数。
输入格式
  输入文件只含一个整数n(1≤n≤18)
输出格式
  输出文件只有一行,即可能输出序列的总数目
样例输入
3
样例输出
5

【思路】这里我用到了卡特兰数,有兴趣的话你也可以用数学归纳推一下。
(n个不同的元素进栈,出栈不同序列个数有(1/1+n)*C(n,2n))

#include<bits/stdc++.h>
using namespace std;
int main(){
	int n;
	long long count;
	cin>>n;
	long long a=1;//分子
	long long b=n+1;//分母 
	long long temp;
	for(int i=0;i<n;i++){
		 temp=2*n-i;
		 a*=temp;
		 temp=n-i;
		 b*=temp;
		 while(a%2==0&&b%2==0){//因为数字很大,只能时间换空间,除二收益很大,得不出答案再除以5,【7 9 11……以此类推】
		 	a/=2;
		 	b/=2;
		 }
		 while(a%3==0&&b%3==0){
		 	a/=3;
		 	b/=3;
		 }
	}
	count=a/b;
	cout<<count<<endl;
	return 0;
} 

在这里插入图片描述

十九、图

二十、堆

二十一、平衡树/线段树

二十二、复杂数据结构

二十三、嵌套数据结构

第八届蓝桥杯大赛软件类决赛

试题A:36进制

[问题描述]
对于16进制,我们使用字母A-F来表示10及以上的数字。如法炮制,一直用到字母Z,就可以表示36进制。36进制中,A表示10,Z表示35,AA表示370你能算出
MANY 表示的数字用10进制表示是多少吗?
[答案提交]
1040254

#include<bits/stdc++.h>
using namespace std;
int main(){
	int num;
	num=('m'-'a'+10)*pow(36,3)+
		('a'-'a'+10)*pow(36,2)+
		('n'-'a'+10)*36+
		('y'-'a'+10);
	cout<<num<<endl;
	return 0;
} 

试题B:磁砖样式

[问题描述] 小明家的一面装饰墙原来是 310 的小方格。现在手头有一批刚好能盖住2个小方格的长方形瓷砖。瓷砖只有两种颜色:黄色和橙色。小明想知道,对于这么简陋的原料,可以贴出多少种不同的花样来。小明有个小小的强迫症:忍受不了任何22的小格子是同一种颜色。(瓷砖不能切割,不能重叠,也不能只铺一部分。另外,只考虑组合图案,请忽略瓷砖的拼缝)显然,对于 23 个小格子来说,口算都可以知道:一共10种贴法,如【p1.png所示】但对于 310 的格子呢?肯定是个不小的数目,请你利用计算机的威力算出该数字。
在这里插入图片描述
[答案提交]

在这里插入代码片

第九届蓝桥杯大赛软件类决赛

试题A:换零钞

[问题描述]
x星球的钞票的面额只有:100元,5元,2元,1元,共4种。 小明去x星旅游,他手里只有2张100元的x星币,太不方便,恰好路过x星银行就去换零钱。小明有点强迫症,他坚持要求200元换出的零钞中2元的张数刚好是1元的张数的10倍, 剩下的当然都是5元面额的。银行的工作人员有点为难,你能帮助算出:在满足小明要求的前提下,最少要换给他多少张钞票吗? (5元,2元,1元面额的必须都有,不能是0)
[答案提交]
74

#include<bits/stdc++.h>
using namespace std;
int main(){
	for(int i=1;i<10;i++){
		int num,sum;
		num=200-21*i;
		num=num/5;
		if(num*5+21*i==200){
			sum=11*i+num;
			cout<<sum<<endl;
		}
	}
	return 0;
} 

试题B:激光样式

[问题描述]
x星球的盛大节日为增加气氛,用30台机光器一字排开,向太空中打出光柱。安装调试的时候才发现,不知什么原因,相邻的两台激光器不能同时打开!国王很想知道,在目前这种bug存在的情况下,一共能打出多少种激光效果?显然,如果只有3台机器,一共可以成5种样式,即:
全都关上(sorry, 此时无声胜有声,这也算一种) 开一台,共3种 开两台,只1种, 30台就不好算了,国王只好请你帮忙了。要求提交一个整数,表示30台激光器能形成的样式种数。
[答案提交]
2178309

#include<bits/stdc++.h>
using namespace std;
int main(){
	int dp[30][2];//30盏灯每盏都可以有两种状态选择 
	dp[0][0]=1;
	dp[0][1]=1;//为第一盏灯初始化
	for(int i=1;i<30;i++){
		dp[i][0]=dp[i-1][0]+dp[i-1][1];//如果当前灯是灭的它前一盏灯可以亮也可以不亮 
		dp[i][1]=dp[i-1][0];//当前灯如果要是亮的,前一盏灯就得是灭的 
	} 
	cout<<dp[29][0]+dp[29][1]<<endl; 
	return 0;
} 

第十届蓝桥杯大赛软件类决赛(难度分水岭)

试题A:平方序列

[问题描述] 小明想找到两个正整数X和Y,满足 ●2019<X< Y; ●2019, x2, Y2组成等差数列。 请你求出在所有可能的解中,X+ Y的最小值是多少?
[答案提交]
7020

#include<bits/stdc++.h>
using namespace std;
int main(){
	bool flag=true;
	int x=2020;
	int num1,num2;
	while(flag){
		num1=2*x*x-2019*2019;
		num2=sqrt(num1);
		if(num2*num2==num1){
			cout<<num2+x<<endl;
			flag=false;
		}
		x++;
	}
} 

试题B:质数拆分

[问题描述] 将2019拆分为若千个两两不同的质数之和,一共有多少种不同的方法?
注意交换顺序视为同一种方法,例如2+ 2017= 2019与2017 +2= 2019视为同一种方法。
[答案提交]
55965365465060

解析:
这是一个jave关于这道题的解析,里面的一张图示我觉得超赞就摘录了
在这里插入图片描述

#include<bits/stdc++.h>
#include<vector>
using namespace std;

//找出2019中(2-2019每个数字中有几个质数,选择或者不选择) 
bool fun(int n){
	for(int i=2;i<=sqrt(n);i++){
		if(n%i==0){
			return false;
		}
	}
	return true;
}
int main(){
	vector<int> V;
	long long int dp[3000];
	for(int i=2;i<=2019;i++){
		if(fun(i)){
			V.push_back(i);
		}
	}
	memset(dp,0,sizeof(dp));
	dp[0]=1;
	for(int i=0;i<V.size();i++){
		for(int j=2019;j>=V[i];j--){
			dp[j]=dp[j]+dp[j-V[i]];
		}
	}
	cout<<dp[2019]<<endl;
	return 0;
} 

第十一届蓝桥杯大赛软件类决赛

试题A:美丽的2

[问题描述] 小蓝特别喜欢2,今年是公元2020年,他特别高兴。他很好奇,在公元1年到公元2020年(包含)中,有多少个年份的数位中 包含数字2?
[答案提交]
563

#include<bits/stdc++.h>
using namespace std;
int main(){
	int q,b,s,g;
	int ans=0;
	for(int i=1;i<=2020;i++){
		q=i/1000;
		b=i%1000/100;
		s=i%100/10;
		g=i%10;
		if(q==2||b==2||s==2||g==2){
			ans++;
		}
	}
	cout<<ans<<endl;
	return 0;
}

试题B:扩散(BFS)

[问题描述] 小蓝在一张无限大的特殊画布上作画。 这张画布可以看成一个方格图,每个格子可以用一个二维的整数坐标表示。小蓝在画布上首先点了一下几个点: (0,0), (2020, 11), (11, 14), (2000, 2000)。只有这几个格子上有黑色,其它位置都是白色的。 每过一分钟,黑色就会扩散一点。具体的,如果一个格子里面是黑色,它就会扩散到上、下、左、右四个相邻的格子中,使得这四个格子也变成黑色 (如果原来就是黑色,则还是黑色)。请问,经过2020分钟后,画布上有多少个格子是黑色的。
[答案提交]
20312088

#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
int m[6100][6100];
int ans;
const int a=2021;
int fx[4][2]={{1,0},{0,1},{0,-1},{-1,0}};
struct node{
	int x,y;
	int time;
};
void bfs(){
	queue<node> Q;
	m[0+a][0+a]=1;
	m[2020+a][11+a]=1;
	m[11+a][14+a]=1;
	m[2000+a][2000+a]=1;
	node n1,n2,n3,n4;
	n1.x=0+a;n1.y=0+a;n1.time=0;
	Q.push(n1);
	n2.x=2020+a;n2.y=11+a;n2.time=0;
	Q.push(n2);
	n3.x=11+a;n3.y=14+a;n3.time=0;
	Q.push(n3);
	n4.x=2000+a;n4.y=2000+a;n4.time=0;
	Q.push(n4);
	ans=4;
	node temp,next;
	while(!Q.empty()){
		temp=Q.front();
		Q.pop();
		if(temp.time==2020){
			cout<<ans<<endl;
			return;
		}
		for(int i=0;i<4;i++){
			next.x=temp.x+fx[i][0];
			next.y=temp.y+fx[i][1];
			if(!m[next.x][next.y]){
				ans++;
				next.time=temp.time+1;
				m[next.x][next.y]=1;
				Q.push(next);
			}
		}
	}
}
int main(){
	memset(m,0,sizeof(m));
	bfs();
	return 0;
}

试题C:阶乘约数

[问题描述] 定义阶乘n!= 1x2x3x…Xn。
请问100! (100 的阶乘)有多少个正约数。
[答案提交]
39001250856960000

#include<bits/stdc++.h>
using namespace std;
int num_1[105];
int main(){
	long long int sum=1;
	memset(num_1,0,sizeof(num_1));
	for(int i=2;i<=100;i++){
		int num=i;
		for(int j=2;j<=sqrt(i);j++){
			while(num%j==0){
				num_1[j]++;
				num=num/j;
			}
		}
		if(num>1){
			num_1[num]++;
		}
	}
	for(int i=2;i<=100;i++){
		if(num_1[i]!=0){
			sum=sum*(num_1[i]+1);	
		}	
	}
	cout<<sum<<endl;
}

试题 D: 本质上升序列(与求子集问题相似)

【问题描述】
小蓝特别喜欢单调递增的事物。
在一个字符串中,如果取出若干个字符,将这些字符按照在字符串中的顺序排列后是单调递增的,则成为这个字符串中的一个单调递增子序列。
例如,在字符串 lanqiao 中,如果取出字符 n 和 q,则 nq 组成一个单调递增子序列。类似的单调递增子序列还有 lnq、i、ano 等等。
小蓝发现,有些子序列虽然位置不同,但是字符序列是一样的,例如取第二个字符和最后一个字符可以取到 ao,取最后两个字符也可以取到 ao。小蓝认为他们并没有本质不同。
对于一个字符串,小蓝想知道,本质不同的递增子序列有多少个?
例如,对于字符串 lanqiao,本质不同的递增子序列有 21 个。它们分别是 l、a、n、q、i、o、ln、an、lq、aq、nq、ai、lo、ao、no、io、lnq、anq、lno、ano、aio。
请问对于以下字符串(共 200 个小写英文字母,分四行显示):
tocyjkdzcieoiodfpbgcncsrjbhmugdnojjddhllnofawllbhf
iadgdcdjstemphmnjihecoapdjjrprrqnhgccevdarufmliqij
gihhfgdcmxvicfauachlifhafpdccfseflcdgjncadfclvfmad
vrnaaahahndsikzssoywakgnfjjaihtniptwoulxbaeqkqhfwl
本质不同的递增子序列有多少个?
[答案提交]
3616159

#include<iostream>
#include<vector>
#include<string>
#include<numeric>
using namespace std;
string s;
int main()
{
	cin>>s;
	int dp[220];
    for(int i=0;i<s.size();++i){
        dp[i] = 1;
    }
    for(int i=1;i<s.size();++i){
        for(int j=0;j<i;++j){
            if(s[i]==s[j]){
                dp[i]-=dp[j];
            }
            if(s[i]>s[j]){
                dp[i] += dp[j];
            }
        }
    }
     cout<<accumulate(dp,dp+s.size(),0);
	return 0;
}
  • 4
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值