【蓝桥集训】第五天——递推

作者:指针不指南吗
专栏:Acwing 蓝桥集训每日一题

🐾或许会很慢,但是不可以停下来🐾

文章目录


递推算法是一种简单的算法,通过已知条件,利用特定关系得出中间推论,逐步递推,直到得到结果为止

1.砖块

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

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

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

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

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

输入格式

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

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

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

输出格式

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

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

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

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

数据范围

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

输入范围:

4
8
BWWWWWWB
4
BWBB
5
WWWWW
3
BWB

输出样例:

3
6 2 4
-1
0
2
2 1
  • 思路

    • 最后的结果可以分成两种情况:全白或者是全黑;

    • 我们操作的位置只有 n-1 种,所以不用考虑 3n 的情况;

    • 每个位置反转两次,相当于没有反转,所以我们只要考虑每个位置要不要操作就可以(0或者1);

    • 如果 i 位置和我们想要的颜色不同,则操作,否则,不,所以说每个位置的操作都是确定的;

    把每一个位置递推一下,到最后一个

    • 最后一个在前一个位置操作或者是没有操作过后,不能操作:判断一下,如果与第一个相同,则说明符合题意。
  • 代码实现

    #include<bits/stdc++.h>
    using namespace std;
    
    int n;
    
    void update(char &a)
    {
    	if(a=='W') a='B';
    	else a='W';
    }
    
    bool check(string s,char x)
    {
    	vector<int> ans; //数组 ans 存操作的位置 
    	for(int i=0;i+1<n;i++){  
    		if(s[i]!=x){  //如果不是我们想要的,则更新
    			update(s[i]);
    			update(s[i+1]);
    			ans.push_back(i);  //把操作位置放入数组中
    		}
    	}
    	if(s.back()!=s[0]) return false;  //最后一个无法改变,如果与前面不同,则不能实现
    	cout<<ans.size()<<endl;
    	for(auto i:ans) cout<<i+1<<' ';  //输出结果
    	return true;
    }
    
    int main()
    {
    	int T;
    	cin>>T;
    	while(T--){
    		string s;
    		cin>>n>>s;
    		if(!check(s,'W')&&!check(s,'B')) puts("-1");  //如果全白或者是全黑都不可以,则输出 -1;
    	} 
    	return 0;
     } 
    

2.翻硬币

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

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

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

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

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

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

输入格式

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

输出格式

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

数据范围

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

输入样例1:

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

输出样例1:

5

输入样例2:

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

输出样例2:

1
  • 思路

    这个题其实很简单,和上一个很类似,几乎一模一样。

    翻硬币,翻一个位置,那么操作的时候,必须这个和下一个连体操作。

    由于,一个位置上的硬币,翻两次相当于没有翻,所以一个硬币只有翻一次和不翻两种情况。

    从第一个位置上开始,如果和目标字符不一样就翻,一样则下一个,操作一次cnt++,最后输出cnt。

  • 代码实现

    #include<bits/stdc++.h>
    using namespace std;
    
    string a,b;  
    
    void update (char &c)   //对某一硬币操作的时候,这里一定要引用!!!不然相当于没改!!!
    {
        if(c=='o') c='*';
        else c='o';
    }
    int main()
    {
       cin>>a;
       cin>>b;  //读入数据
       
       int n=a.size();  //n表示硬币数
       int cnt=0;
       for(int i=0;i<n;i++)  //从第一个位置上的硬币开始,和目标字符一样,则下一个;不一样,则操作
       {
           if(a[i]!=b[i]){
               update(a[i]);
               update(a[i+1]);
               cnt++;  //记录操作的次数
           }
       }
       cout<<cnt;   //输出结果
       
        return 0;
    }
    

Alt

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

指针不指南吗

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值