题目练习

127 阅读3分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

蓝桥杯练习030

顺时针打印二维数组

代码

#include<iostream>
using namespace std;
void print(int a[][4],int n,int m){
	int leftUpRow=0,leftUpCol=0;//左上角 
	int rightDownRow=3,rightDownCol=3;//右下角 
	//每一圈为一层循环,终止条件是两个位置交错 
	while(leftUpRow<=rightDownRow&&leftUpCol<=rightDownCol){
		int r=leftUpRow,c=leftUpCol;//r为行c为列 
		//打印第r行数据 
		while(c<=rightDownCol)
			cout<<a[r][c++]<<" ";//行不变列增加 
		//接下来打印第c列元素 
		c=rightDownCol; //恢复为最后的列数 
		r++;//行增加,避免重复打印元素
		//打印第c列的元素 
		while(r<=rightDownRow)//列不变行增加 
			cout<<a[r++][c]<<" ";
		r=rightDownRow;//恢复 
		c--;
		while(c>=leftUpCol)//从右往左打印第r行 
			cout<<a[r][c--]<<" ";
		c=leftUpCol;//恢复 
		r--;
		while(r>leftUpRow)//从下往上打印第c列 
			cout<<a[r--][c]<<" ";
		//一圈打印完成后,打印内部的 
		leftUpRow++;
		leftUpCol++;
		rightDownRow--;
		rightDownCol--; 
	}
	
	
}
int main()
{
	int n,m;
	//cin>>n>>m;
	int a[4][4]={
		{1,2,3,4},
		{5,6,7,8},
		{9,10,11,12},
		{13,14,15,16}
	};
	print(a,4,4);
	return 0;
}

手动输入

#include<iostream>
using namespace std;

void Print(int **a,int n,int m){
	int leftUpRow=0,leftUpCol=0;
	int rightDownRow=n-1,rightDownCol=m-1;
	while(leftUpRow<=rightDownRow&&leftUpCol<=rightDownCol){
		int r=leftUpRow,c=leftUpCol;
		while(c<=rightDownCol)
			cout<<a[r][c++]<<" ";
		c=rightDownCol;
		r++;
		while(r<=rightDownRow)
			cout<<a[r++][c]<<" ";
		r=rightDownRow;
		c--;
		while(c>=leftUpCol)
			cout<<a[r][c--]<<" ";
		c=leftUpCol;
		r--;
		while(r>leftUpRow)
			cout<<a[r--][c]<<" ";
		leftUpRow++;
		leftUpCol++;
		rightDownRow--;
		rightDownCol--;
	}
	
}
int main()
{
	int n,m;
	cin>>n>>m;
	int **arr=new int*[n];
	for(int i=0;i<n;i++){
		arr[i]=new int[m];
	} 
	for(int i=0;i<n;i++)
		for(int j=0;j<m;j++)
			cin>>arr[i][j];
	Print(arr,n,m);
	return 0;
}

0所在行列清零

代码

#include<iostream>
using namespace std;

void solve(int **a,int n,int m){
	int *rowRecord=new int[n];//标记有0的行 
	int *colRecord=new int[m];//标记有0的列 
	for(int i=0;i<n;i++){
		for(int j=0;j<m;j++){
			if(a[i][j]==0){//原数组有0 
				rowRecord[i]=1;
				colRecord[j]=1;
			}
		}
	}
	//对辅助空间进行搜索
	//如果有标记的1,则把原数组的行和列全部清0 
	for(int row=0;row<n;row++){
		for(int col=0;col<m;col++){
			if(rowRecord[row]==1||colRecord[col]==1){
				a[row][col]=0;
			}
		}
	}
}
int main()
{
	int n,m;
	cin>>n>>m;
	int **a=new int* [n];
	for(int i=0;i<n;i++){
		a[i]=new int[m];
	}
	for(int i=0;i<n;i++){
		for(int j=0;j<m;j++)
			cin>>a[i][j];
	}
	solve(a,n,m);
	for(int i=0;i<n;i++){
		for(int j=0;j<m;j++)
			cout<<a[i][j]<<" "; 
		cout<<endl;
	}
	return 0;
}

蓝桥杯练习031

跳石头

题目链接:跳石头 - 蓝桥云课 (lanqiao.cn)

题目描述

一年一度的"跳石头"比赛又要开始了!

这项比赛将在一条笔直的河道中进行,河道中分布着一些巨大岩石。组委会已经选择好了两块岩石作为比赛起点和终点。在起点和终点之间,有 N块岩石(不含起点和终点的岩石)。在比赛过程中,选手们将从起点出发,每一步跳向相邻的岩石,直至到达终点。

为了提高比赛难度,组委会计划移走一些岩石,使得选手们在比赛过程中的最短跳跃距离尽可能长。由于预算限制,组委会至多从起点和终点之间移走M 块岩石(不能移走起点和终点的岩石)。

输入描述

输入文件第一行包含三个整数 L,N,M,分别表示起点到终点的距离,起点和终点之间的岩石数,以及组委会至多移走的岩石数。

接下来 N行,每行一个整数,第 i行的整数 Di(0 < Di < L)表示第 i块岩石与起点的距离。这些岩石按与起点距离从小到大的顺序给出,且不会有两个岩石出现在同一个位置。

其中,0≤MN≤5×10^4, 1≤L≤10^9。

输出描述

输出只包含一个整数,即最短跳跃距离的最大值。

输入输出样例

示例

输入

25 5 2
2
11
14
17
21

输出

4

运行限制

  • 最大运行时间:1s
  • 最大运行内存: 256M

解题思路 - C++

在 n块岩石中移走 m个石头,有很多种移动方法。在第 i种移动方法中,剩下的石头之间的距离,有一个最小距离 ai。在所有移动方法的最小距离 ai 中,问最大的 ai是多少。暴力破解会超时,可以用二分法试探可以的距离。

用一个变量记录当前所在的位置,一个变量记录搬走石头的数量,当可以搬走某个石头时,增加1,否则就将位置更新为该块石头的位置。

代码

#include<iostream>
using namespace std;

int L,N,M;
int a[50005];
//检查距离d是否合适 
bool check(int d){
	int num=0;//记录搬走石头数量 
	int pos=0;//当前位置 
	for(int i=1;i<=N;i++){
		if(a[i]-pos<d){//第i块石头可以搬走 
			num++;
		}else{
			pos=a[i];//第i块石头不能搬走 
		}
	}
	if(num<=M)return true;//搬不多于M块石头,满足条件 
	else return false;//不满足条件 
	
}
int main()
{
	cin>>L>>N>>M;
	for(int i=1;i<=N;i++)
		cin>>a[i];
	int left=0,right=L,mid;
	while(left<right){
		mid=left+((right-left)>>1);
		if(check(mid))//当前距离可以 
			left=mid+1;//试探下一个 
		else//不满足,mid大了 
			right=mid-1;//尝试小的 
	}
	if(!check(left))
		left--;
	cout<<left<<endl;
	return 0;
}

蓝桥杯练习032

字符串匹配

暴力匹配

代码

int indexOf1(string s,string p){
	int i=0;//s 
	int j=0;//p
	int scan=i;//辅助指针 
	while(scan<s.length()){//对s进行扫描 
		if(s[scan]==p[j]){//scan和j位置字符相同 
			scan++;
			j++;
			//j和p的长度相同,说明匹配成功,返回i 
			if(j==p.length())return i;
		}else{//进行下一轮,i++ 
			i++;
			scan=i;//更新scan 
			j=0;//j从头开始 
		}
	}
	//没有匹配到 
	return -1;
	
} 

kmp

代码-Next

int *Next(string ps){
	int plen=ps.length();
	int *next=new int[plen];
	string p=ps;
	next[0]=-1;
	if(ps.length()==1)return next;
	next[1]=0;
	int j=1;
	int k=next[j];//看看位置j的最长匹配前缀在哪
	while(j<plen-1){
		//现在要推出next[j+1],检查j和k位置上的关系
		if(k<0||p[j]==p[k]){
			next[++j]=++k;
		}else{
			k=next[k];
		}
	} 
	return next;
}

代码-kmp

int indexOf2(string s,string p){
	if(s.length()==0||p.length()==0)return -1;
	if(p.length()>s.length())return -1;
	int *next=Next(p);
	int i=0;
	int j=0;
	int slen=s.length();
	int plen=p.length();
	while(i<slen){
		//如果j=-1或者当前字符匹配成功
		//s[i]==p[j] i++,j++
		//j=-1因为next[0]=-1,说明p的第一位和i这个位置无法匹配
		//i和j都增加1,i移位,j从0开始
		if(j==-1||s[i]==p[j]){
			i++;
			j++;
		}else{
			//如果j!=-1,且当前字符匹配失败
			//s[i]!=p[j] i不变,j=next[j]
			//next[j]为j所对应的next值 
			j=next[j];
		} 
		if(j==plen)
			return i-j;
	}
	return -1;
}

测试

int main()
{
	string s="babababcbabababb";
	string p="bababb";
	cout<<indexOf1(s,p)<<endl;
	cout<<indexOf2(s,p)<<endl;
	return 0;
}

完整代码

#include<iostream>
#include<string>
using namespace std;
//暴力匹配 
int indexOf1(string s,string p){
	int i=0;//s 
	int j=0;//p
	int scan=i;//辅助指针 
	while(scan<s.length()){//对s进行扫描 
		if(s[scan]==p[j]){//scan和j位置字符相同 
			scan++;
			j++;
			//j和p的长度相同,说明匹配成功,返回i 
			if(j==p.length())return i;
		}else{//进行下一轮,i++ 
			i++;
			scan=i;//更新scan 
			j=0;//j从头开始 
		}
	}
	//没有匹配到 
	return -1;
	
} 

int *Next(string ps){
	int plen=ps.length();
	int *next=new int[plen];
	string p=ps;
	next[0]=-1;
	if(ps.length()==1)return next;
	next[1]=0;
	int j=1;
	int k=next[j];//看看位置j的最长匹配前缀在哪
	while(j<plen-1){
		//现在要推出next[j+1],检查j和k位置上的关系
		if(k<0||p[j]==p[k]){
			next[++j]=++k;
		}else{
			k=next[k];
		}
	} 
	return next;
}

//kmp 
int indexOf2(string s,string p){
	if(s.length()==0||p.length()==0)return -1;
	if(p.length()>s.length())return -1;
	int *next=Next(p);
	int i=0;
	int j=0;
	int slen=s.length();
	int plen=p.length();
	while(i<slen){
		//如果j=-1或者当前字符匹配成功
		//s[i]==p[j] i++,j++
		//j=-1因为next[0]=-1,说明p的第一位和i这个位置无法匹配
		//i和j都增加1,i移位,j从0开始
		if(j==-1||s[i]==p[j]){
			i++;
			j++;
		}else{
			//如果j!=-1,且当前字符匹配失败
			//s[i]!=p[j] i不变,j=next[j]
			//next[j]为j所对应的next值 
			j=next[j];
		} 
		if(j==plen)
			return i-j;
	}
	return -1;
}

int main()
{
	string s="babababcbabababb";
	string p="bababb";
	cout<<indexOf1(s,p)<<endl;
	cout<<indexOf2(s,p)<<endl;
	return 0;
}

蓝桥杯练习033

程序员面试金典

第8章 面试考题

8.9递归和动态规划

题目要求

实现一种算法,打印n对括号的全部有效组合(即左右括号正确配对)。

示例

输入 :3 输出 :((())) (()()) (())() ()(()) ()()() (第230页)

解题思路

输入很简洁,输入的n表示要打印n对括号的全部有效组合
可以考虑用递归求解:
	1.n是数据的规模,当n足够小时很容易解决
	2.当n为1时,也就是()这种情况,此时只有一种,也就是出口
	3.n大于1的时候,要考虑括号的插入位置:
		3.1可以插入到当前括号的左侧
		3.2可以插入到当前括号的右侧
		3.3可以插入到当前括号的两侧,即左右各一个
	4.考虑到插入过程中有重复的情况,引入set,去重
	5.对set遍历需要用到迭代器
	

代码

#include<iostream>
#include<set>
#include<string>
using namespace std;
set<string> parenthesis(int n) {
	set<string> res;//保存上次迭代状态
	res.insert("()");//初始状态只有一对括号
	if (n == 1) {
		return res;//n为1时直接返回
	}
	for (int i = 2; i <= n; i++) {//n大于2时
		set<string> res_new;//创建辅助空间
		set<string>::iterator it = res.begin();//迭代器
		for (; it != res.end(); it++) {
			//向res_new中插入括号,插入的位置分别是左侧,右侧,还有两头
			res_new.insert(*it + "()");
			res_new.insert("()" + *it);
			res_new.insert("(" + *it + ")");
		}
		res = res_new;//更新res
	}
	return res;//返回res
}
set<string> parenthesis1(int n) {
	set<string> s_n;
	if (n == 1) {
		s_n.insert("()");//插入括号
		return s_n;
	}
	//递归求解n-1情况
	set<string> s_n_1 = parenthesis1(n - 1);
	set<string>::iterator it = s_n_1.begin();//迭代器
	for (; it != s_n_1.end(); it++) {
		s_n.insert("()" + *it);
		s_n.insert(*it + "()");
		s_n.insert("(" + *it + ")");
	}
	return s_n;
}

int main()
{
	int n;
	cin >> n;
	set<string> se = parenthesis(n);
	set<string>::iterator it = se.begin();
	for (; it != se.end(); it++)
		cout << *it << " ";
	cout << endl;
	se = parenthesis1(n);
	it = se.begin();
	for (; it != se.end(); it++)
		cout << *it << " ";
	cout << endl;
	return 0;
}