P9574 「TAOI-2」Break Through the Barrier

去洛谷看我的博客

思路

首先我们可以肯定的是,无论如何变化,答案最多比原序列的连续 T T T 的个数多 2 2 2

理由很简单,对于 . . . B T . . . T B . . . ...BT...TB... ...BT...TB...,最好的可能就是前后两个 B B B 可以变成 T T T,因为只可能是 B T T B BTTB BTTB 变成 T B B T TBBT TBBT,所以变了以后再外面就一定是 B B B 了,且无法再变。

所以我们可以先找到可能成为答案的一段连续 T T T,然后暴力向前和向后判断能否增加答案,然后更新答案即可。

那么,什么时候可以增加答案呢?

当然是前面或者后面是 B T T B BTTB BTTB 的时候啦,于是我满心欢喜地以为自己找到了正解,然后交了一发,然后 WA 了。

嗯,当时自己确实有点狂妄,都没发现样例最后一组都错了。

再仔细观察了样例后,发现如果出现了 B T T B T B T B T B . . . BTTBTBTBTB... BTTBTBTBTB... 的情况,就可以从左至右依次变化,最后也能使答案增加。

所以形如前面有至少一个 B T BT BT,后面有若干个 T B TB TB 也能增加答案,反过来也同理。

所以我又不假思索地直接写了发暴力判断前后能否增加答案,然后 TLE 了。

嗯,至少没有 WA,这至少是一件好事,代表这个做法没有问题,那么如何优化呢,思考了一下,如果出现了 B T T B T B T B T B . . . BTTBTBTBTB... BTTBTBTBTB... 的情况,每个 T T T 都会去暴力判断一次(因为最长 T T T 的长度是 2 2 2,那所有长度为 1 1 1 的都有可能加 2 2 2 超过最长的,所以必须判断),那这样的话就重复判断了很多次。如果数据比较特殊,那必然会 TLE。

所以我们需要优化代码,来让上面的情况不会出现。

于是,我想到了预处理。

只要出现了 B T BT BT,那么,后面的所有连续的 T B TB TB B B B 位置都可以变成 T T T 来让答案增加一,反过来也差不多,就跑两边就好。

只是这次我看了下样例,居然错了,形如 B T T B BTTB BTTB 的测试点,程序会把左右两个 B B B 都搞成可以增加答案的情况了。

所以,我们还需要区分是方向,就用两个数组存就行。

终于,在这一波三折的猪脑过载调试下,终于把这道题目过了。

AC 代码

#include<bits/stdc++.h>
using namespace std;
int T,n,ans,num,p,kkk;
bool can1[100005],can2[100005];
char ch[100005];
int main()
{
	scanf("%d",&T);
	while(T--)
	{
		memset(can1,0,sizeof(can1)),memset(can2,0,sizeof(can2));//记得清零
		scanf("%d%s",&n,ch),p=-1,ans=num=0;//记得赋初始值
		for(int i=0;i<n;++i)
		{
			if(ch[i]=='B'&&ch[i+1]=='T')//如果出现了BT
			{
				int kkk=i+2;
				while(kkk<n-1&&ch[kkk]=='T'&&ch[kkk+1]=='B')
					can1[kkk+1]=1,i=kkk,kkk+=2;//就把所有后面连续的TB的B位置上标记为正向可增加答案
			}
		}
		for(int i=n-1;i>=0;--i)
		{
			if(ch[i]=='B'&&ch[i-1]=='T')//同上,只是顺序反了
			{
				int kkk=i-2;
				while(kkk>0&&ch[kkk]=='T'&&ch[kkk-1]=='B')
					can2[kkk-1]=1,i=kkk,kkk-=2;
			}
		}
		//这里的kkk赋值给i是为了让循环少几次,不然就和直接暴力没什么区别了
		for(int i=0;i<n;++i)
		{
			if(ch[i]=='T')//如果是T就增加,p是为了标记这段连续的T的第一个位置
			{
				++num;
				if(p==-1) p=i;
			}
			else
			{
				/*下面三行可替换为ans=max(ans,num+can1[p-1]+can2[i]);*/ 
				if(can1[p-1]) ++num;
				if(can2[i]) ++num;
				ans=max(ans,num);
				num=0,p=-1; 
			}
		}
		/*下面四行可替换为printf("%d\n",max(ans,num+can1[p-1]+can2[n-1]));*/
		if(can1[p-1]) ++num;
		if(can2[n-1]) ++num;
		ans=max(ans,num);
		printf("%d\n",ans);
		//最后还可能有一段没有判断到,需要再max一遍
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值