蓝桥杯-递归递推专题(包含历年蓝桥杯真题和详细注释)

目录

砖块

 翻硬币(第四届蓝桥杯省赛C++B组)

 树的遍历


砖块

nn 个砖块排成一排,从左到右编号依次为 1∼n1∼n。

每个砖块要么是黑色的,要么是白色的。

现在你可以进行以下操作若干次(可以是 00 次):

选择两个相邻的砖块,反转它们的颜色。(黑变白,白变黑)

你的目标是通过不超过 3n3n 次操作,将所有砖块的颜色变得一致。

输入格式

第一行包含整数 TT,表示共有 TT 组测试数据。

每组数据第一行包含一个整数 nn。

第二行包含一个长度为 nn 的字符串 ss。其中的每个字符都是 W 或 B,如果第 ii 个字符是 W,则表示第 ii 号砖块是白色的,如果第 ii 个字符是 B,则表示第 ii 个砖块是黑色的。

输出格式

每组数据,如果无解则输出一行 −1−1。

否则,首先输出一行 kk,表示需要的操作次数。

如果 k>0k>0,则还需再输出一行 kk 个整数,p1,p2,…,pkp1,p2,…,pk。其中 pipi 表示第 ii 次操作,选中的砖块为 pipi 和 pi+1pi+1 号砖块。

如果方案不唯一,则输出任意合理方案即可。

数据范围

1≤T≤101≤T≤10,
2≤n≤2002≤n≤200。

输入样例:

4
8
BWWWWWWB
4
BWBB
5
WWWWW
3
BWB

输出样例:

3
6 2 4
-1
0
2
2 1

解析:

#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
int n,T;
string str;
void update(char &c)
{//交换字母 如果是W就改为B 如果是B就改为W
    if(c=='W')c='B';
    else c='W';
}
bool check(char c)
{
    string s=str;
    vector<int>ans;//用来存取进行变换的下标
    for(int i=0;i+1<n;i++)
    {
        if(s[i]!=c){//从左到右遍历 如果当前字符不等于目标字符 则将当前字符和后面一个字符进行转换
            update(s[i]);
            update(s[i+1]);
            ans.push_back(i);//将当前变换下标进行存储
        }
    }
    if(s.back()!=c)return false;//最后一个字符不等于目标字符 则表示 无法全部转换为c
    cout<<ans.size()<<endl;//转换次数
    for(auto x:ans)cout<<x+1<<' ';//因为题目下标由1开始 记得+1
    if(ans.size())cout<<endl;
    return true;
}
int main()
{
    cin>>T;
    while (T -- ){
        cin>>n>>str;
        if(!check('B')&&!check('W'))cout<<-1<<endl;//如果将字符串转换为全B和全W都失败 则无解输出-1
    }
    return 0;
}

 翻硬币(第四届蓝桥杯省赛C++B组)

小明正在玩一个“翻硬币”的游戏。

桌上放着排成一排的若干硬币。我们用 * 表示正面,用 o 表示反面(是小写字母,不是零)。

比如,可能情形是:**oo***oooo

如果同时翻转左边的两个硬币,则变为:oooo***oooo

现在小明的问题是:如果已知了初始状态和要达到的目标状态,每次只能同时翻转相邻的两个硬币,那么对特定的局面,最少要翻动多少次呢?

我们约定:把翻动相邻的两个硬币叫做一步操作。

输入格式

两行等长的字符串,分别表示初始状态和要达到的目标状态。

输出格式

一个整数,表示最小操作步数

数据范围

输入字符串的长度均不超过100。
数据保证答案一定有解。

输入样例1:

**********
o****o****

输出样例1:

5

输入样例2:

*o**o***o***
*o***o**o***

输出样例2:

1

解析:

#include<iostream>
using namespace std;
string a,b;
int main()
{
    cin>>a>>b;
    int n=a.size();
    int ans=0;
    //从左往右遍历
    for(int i=0;i+1<n;i++)
    {//当前字符与目标字符不同
        if(a[i]!=b[i]){
            if(a[i]=='*')a[i]='o';
            else a[i]='*';//翻转当前硬币
            if(a[i+1]=='*')a[i+1]='o';
            else a[i+1]='*';//翻转下一个硬币
            ans++;//次数+1
        }
    }
    cout<<ans<<endl;
    return 0;
}

 树的遍历

一个二叉树,树中每个节点的权值互不相同。

现在给出它的后序遍历和中序遍历,请你输出它的层序遍历。

输入格式

第一行包含整数 NN,表示二叉树的节点数。

第二行包含 NN 个整数,表示二叉树的后序遍历。

第三行包含 NN 个整数,表示二叉树的中序遍历。

输出格式

输出一行 NN 个整数,表示二叉树的层序遍历。

数据范围

1≤N≤301≤N≤30,
官方并未给出各节点权值的取值范围,为方便起见,在本网站范围取为 1∼N1∼N。

输入样例:

7
2 3 1 5 7 6 4
1 2 3 4 5 6 7

输出样例:

4 1 6 3 5 7 2

解析:

#include<iostream>
#include <vector>
using namespace std;
const int N = 35;
vector<int>level[N];
int n;
int a[N],b[N],p[N];//a记录后序 b记录中序 p[i]记录i号结点在中序遍历的位置下标
void build(int al,int ar,int bl,int br,int d)
{//al 为后序遍历左端点 ar为后序遍历右端点 bl为中序遍历左端点 br为中序遍历右端点 d为层数
    if(al>ar)return ;//当左端点大于右端点 
    int val=a[ar];//根节点为后续遍历的最后一个结点
    level[d].push_back(val);//当前层加入结点 因为层序遍历 所以下面要先遍历左节点在遍历右节点
    int k=p[val];//找到val在中序遍历的下标
    build(al,k-1-bl+al,bl,k-1,d+1);//遍历左子树 
    //左子树--后序的左端点不变 中序左端点不变 中序右端点为k-1 层数+1
    //左子树--后序右端点-后序左端点=中序右端点-中序左端点  得到后序右端点=中序右端点-中序左端点+后序左端点 即k-1-bl+al
    build(k-bl+al,ar-1,k+1,br,d+1);//遍历右子树
    //右子树的后序左端点为左子树后序右端点+1 右端点为ar-1 中序遍历右子树左端点为k+1 右端点为br 层数+1
    
    //最难就在于求出后序遍历左子树的右端点 这边建议画一个图会清晰很多
}
int main()
{
    cin>>n;
    for (int i = 0; i < n; i ++ )cin>>a[i];
    for (int i = 0; i < n; i ++ )cin>>b[i];
    for (int i = 0; i < n; i ++ )p[b[i]]=i;//记录值为b[i]在b中的下标
    build(0,n-1,0,n-1,0);
    for(int i=0;i<n;i++)
        for(auto x:level[i])//按序输出每一层的结果
            cout<<x<<' ';
        
    return 0;
}

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值