本文已参与「新人创作礼」活动,一起开启掘金创作之路。
蓝桥杯练习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
跳石头
题目描述
一年一度的"跳石头"比赛又要开始了!
这项比赛将在一条笔直的河道中进行,河道中分布着一些巨大岩石。组委会已经选择好了两块岩石作为比赛起点和终点。在起点和终点之间,有 N块岩石(不含起点和终点的岩石)。在比赛过程中,选手们将从起点出发,每一步跳向相邻的岩石,直至到达终点。
为了提高比赛难度,组委会计划移走一些岩石,使得选手们在比赛过程中的最短跳跃距离尽可能长。由于预算限制,组委会至多从起点和终点之间移走M 块岩石(不能移走起点和终点的岩石)。
输入描述
输入文件第一行包含三个整数 L,N,M,分别表示起点到终点的距离,起点和终点之间的岩石数,以及组委会至多移走的岩石数。
接下来 N行,每行一个整数,第 i行的整数 Di(0 < Di < L)表示第 i块岩石与起点的距离。这些岩石按与起点距离从小到大的顺序给出,且不会有两个岩石出现在同一个位置。
其中,0≤M≤N≤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;
}