这个题来看的话好像有点难下手,不如再去读一遍题 N遍,发现一句话很重要Each time the machine is used, it reverses the facing direction of a contiguous group of K cows in the line,就是说只能翻转固定的长度区间,那这样是否是就能够枚举区间了?枚举一层区间,再枚举每次起点,最后加上区间修改,时间复杂度\(O(N^3)\),确定会T掉,接下来就考虑优化了。
优化怎么入手呢?时间主要就是出在这三层循环上,只要省掉一层循环,时间复杂度就能到\(O(N^2)\),这样就能够过,第一层循环,显然不能省略,第二层一样,只有在区间修改这一层循环上能够作点手脚,回忆区间修改,有几种作法,线段树,树状数组,还有差分,前二者用在这都有点大材小用或是说不是很合适,由于判断是否区间修改完成很差判断,而差分用在这个区间上就很合适了。那咱们大概思路也就有了,首先读入数组,将B标记成1,F标记成0,这里怎么标记都无所谓,而后利用枚举区间,差分修改,最后输出答案,下面考虑一下细节。
咱们枚举区间完,要从左到右一次反转区间,为何呢?题目中要求的是最小次数,就是要先保证次数最小,再考虑区间长度,而咱们若是先修改后面的,把后面改好了,再去改前边的,结果必定不会比先改前边的好(有可能相等,如00100),因此咱们为保证最小次数,必定从最左端开始依次枚举,若是这个点不符合,就把他后面的整个区间翻转,这里就要用到差分了,确定直接修改会T掉,咱们能够考虑,若是这个区间要修改,那么原来的1会变成2,0会变成1,好像没什么规律,但再看就发现全部的奇数都须要改变,偶数就不用,每次修改给整个区间加一,判断奇偶数就行,而后这就变成了一个区间加一个数的操做,相信你们应该都会。这样修改就完成了,那么怎么判断能不能完成题目的任务呢?由题意能够知道若是当前区间长度小于修改的区间长度,是不能修改的,也就是从n往前的长度为len的区间老是没法被修改的,因此判断这一段区间内有无不知足条件的点便可。
最后找答案的时候也有一个地方,就是当操做修改次数不一样时,直接用操做修改次数最小的那个答案就行,但若是当前操做次数和原来答案相同,是否是要考虑一下区间长度改为最小值?答案显然是不是,…………,由于咱们是从小到大枚举的区间长度,因此在遇到相等的时候,已经获得的答案的区间长必定是小的,因此只在次数不一样时修改答案,但判断上也不会错。c++
固然如下优化不加也没问题,毕竟算法时间复杂度足够过掉这道题。
我作完以后看了看时间大概700ms左右,好像有点高,看别人的时间好像没有特别大,因此我加了加小优化。
为方便说,由上到下一次标号\(1-4\),1,2跑的时间仍是挺快的但没啥用,\(NOIp\)不可能给你开O2也不可能给你c++17,因此仍是看一下3和4,这俩时间大概有一倍的关系,看一下代码吧算法
#include<cstdio> #include<cstring> using namespace std; const int N=5e3+10; char s[3]; int cf[N],a[N]; int min(int a,int b){ if(a<b)return a; else return b; } int main(){ int n;scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%s",s); if(s[0]=='B')a[i]=1; else a[i]=0; } int res=0x3f3f3f3f,ans=0x3f3f3f3f; for(int len=1;len<=n;len++){ int cnt=1,k=0;memset(cf,0,sizeof(cf)); for(int i=1;i<=n;i++){ cf[i]+=cf[i-1]; if(i+len-1<=n){ if(a[i]+cf[i]&1){ cf[i]++;cf[i+len]--;k++; } }else if(cf[i]+a[i]&1){cnt=0;break;} } if(cnt) if(k<ans){ ans=k;res=len; } else if(k==ans)res=min(res,len); } printf("%d %d",res,ans); return 0; }
#include<cstdio> #include<cstring> using namespace std; const int N=5e3+10; char s[3]; int cf[N],a[N]; int min(int a,int b){ if(a<b)return a; else return b; } int main(){ int n;scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%s",s); if(s[0]=='B')a[i]=1; else a[i]=0; } int res=0x3f3f3f3f,ans=0x3f3f3f3f; for(int len=1;len<=n;len++){ int cnt=1,k=0;memset(cf,0,sizeof(cf)); for(int i=1;i<=n;i++){ cf[i]+=cf[i-1]; if(i+len-1<=n){ if(a[i]+cf[i]&1){ cf[i]++;cf[i+len]--;k++; } }else if(cf[i]+a[i]&1)cnt=0; } if(cnt) if(k<ans){ ans=k;res=len; } else if(k==ans)res=min(res,len); } printf("%d %d",res,ans); return 0; }
其实就是少一个break,感受这个加上仍是颇有必要的,由于可能极限数据的时候,CCF那评测机状态很差,再卡一下,可能会出问题。数组
那么有没有可能最开始所有是朝前的呢?答案是没有,英文题面中已经讲到,有一些牛,因此说不可能其实所有朝前边的。优化