原题:
迫切希望在郡县集市上赢得最佳奶牛摄影师的农夫约翰正在尝试为他的 NN 头奶牛拍摄一张完美的照片。
农夫约翰拥有两种品种的奶牛:更赛牛(Guernsey)和荷斯坦牛(Holstein)。
为了使他的照片尽可能地艺术,他想把他的奶牛排成一排,使得尽可能多的更赛牛处于队列中的偶数位置(队列中的第一个位置是奇数位置,下一个是偶数位置,以此类推)。
由于他与他的奶牛缺乏有效的沟通,他可以达到目的的唯一方法是让他的奶牛的偶数长的「前缀」进行反转(一个前缀指的是对于某个位置 jj,从第一头奶牛到第 jj 头奶牛范围内的所有奶牛)。
请计算农夫约翰达到目的所需要的最小反转次数。
输入格式
输入的第一行包含 NN 的值。
第二行包含一个长为 NN 的字符串,给出初始时所有奶牛从左到右的排列方式。每个 H
代表一头荷斯坦牛,每个 G
代表一头更赛牛。
输出格式
输出一行,包含达到目的所需要的最小反转次数。
数据范围
2≤N≤2×1052≤N≤2×105,NN 为偶数。
输入样例:
14
GGGHGHHGHHHGHG
输出样例:
1
样例解释
在这个例子中,只需反转由前六头奶牛组成的前缀即可。
GGGHGHHGHHHGHG (反转前)
-> HGHGGGHGHHHGHG (反转后)
在反转之前,四头更赛牛处于偶数位置。
反转后,六头更赛牛处于偶数位置。不可能使得超过六头更赛牛处于偶数位置。
题目分析:
题目中有两种牛:更赛牛G和荷斯坦牛H,将其排成一排,让尽可能多的更赛牛处于队列中的偶数位置,通过反转达到目的,求达到目的的所需的最小反转次数。
明确:
1、只有两种牛G和H,其相邻排列的牛只有4种情况GH、HG、HH、GG
2、让更多的更赛牛处于偶数位置,即有更多的HG的情况(数据范围给出了N头牛的数量为偶数)
3、反转是如何操作的:让奶牛的偶数长的前缀进行反转(一个前缀指的是对于某个位置j,从第一头奶牛到第j头奶牛范围内的所有奶牛)。意思就是说比如j=6,那么从第1头到第6头的所有奶牛都要反转,第1头牛和第6头牛换位置,第2头和第5头换位置,第3头和第4头换位置。以题目中的样例为例子
根据反转前和反转后的字符串的不同,我们可以发现:
①GG和HH反转并不影响它们奇偶数位,比如说GG,排序本来是12,反转之后,排序为2的变成了1,1变成了2,依然是一样的结果
②相邻的两个字符反转之后还是相邻,比如说样例中的第5个和第6个字符GH,反转之后排序到1和2位置变成HG,两个字符的位置反了,但是G和H依然是相邻的
③我们对整个字符串两个两个分,只需要将GH的变成HG就可以使得G处于偶数位
④至于为什么两个两个看,是个很神奇的看法,我也不知道……
我们的做法:
但是我们不能直接将GH反转为HG,因为这不符合题目的反转要求:从第j头牛前面的都要整体反转,所以我们先一步一步先反转着看,还是以样例为例子:
输入样例:
GGGHGHHGHHHGHG
(两个两个看,遇到GG和HH不用管,因为反转过来没有影响,我们主要看GH和HG)
①我们首先遇到的是GH,这里先不反转,后面遇到HG的时候再进行反转,因为我们要求的是最少的反转次数,如果遇到一个就反转的话,反转次数太多,而且也会出现问题,比如(如果遇到就反转的话):
输入 GG GH GH GH HG
第一次反转HG GG GH GH HG
第二次反转HG GG GH GH HG 这里把第一次转的HG又转回去了
第三次反转HG HG GG GH HG 当指针移到最后一对时,它是满足G在偶数位的,但是前面有一对是不满足G在偶数位的
事实上我们只需要反转前面4对就可以使得所有的GH的G处于偶数位,即只需要反转一次
这里插入了一下,我们接着分析例子
②遇到第4对HG(和前面遇到的GH相反)
GG GH GH HG HH HG HG => HG HG GG HG HH HG HG(此时已经将所有的GH变成了HG,而我们只反转了1次)
注意:
我们还应该考虑一种情况,样例中没有出现这种情况,就是我们每次反转都是保证的反转位置前面的最优解,使得前面都保持一样的顺序,要么都是GH,要么都是HG,如果我们最后一次反转之后的结果是都是GH,那么我们还需要额外再反转一次,比如下面的字符串进行最后一次反转:
GG HG HG GH => GH GH GG GH
但是最后反转一次之后,GH中的G并不是处于偶数位,所以还要反转一次
代码如下:
#include <bits/stdc++.h>
using namespace std;
int main()
{
int n;//总共的牛数
string s;//初始时两种牛的排序
cin>>n>>s;
int res=0;//反转次数
int f=0;//标记是HG的情况还是GH的情况 1:gh 2:hg
for(int i=0;i<n;i+=2)
{
//注意字符串数组的下标是从0开始,所以如果是GH情况就不需要反转,如果是HG情况则需要反转
//判断GH的情况
if(s[i]=='G'&&s[i+1]=='H')
{
res+=f==2;
f=1;
}
else if(s[i]=='H'&&s[i+1]=='G')
{
res+=f==1;//直到遇见HG,前面所有的GH才会翻转次数+1
f=2;
}
}
if(f==1) res+=1;//最后再进行一次判断
cout<<res;
}