這個題來看的話好像有點難下手,不如再去讀一遍題 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那評測機狀態很差,再卡一下,可能會出問題。數組
那麼有沒有可能最開始所有是朝前的呢?答案是沒有,英文題面中已經講到,有一些牛,因此說不可能其實所有朝前邊的。優化