题目大意:
给你一个长度为n的字符串,包含字母F和B
你可以把区间k(一个常数)内的所有F变成B,B变成F。
为了把这个字符串都变成F,求变化的最小次数和其对应的k的值
分析:
《挑战程序设计竞赛》反转法的例题,(此做法非书上做法)
枚举k,对于每个k,
只要序列最左端的B变成F,然后依次变化,得到答案:
枚举起点然后把k个字母进行变化,这个揭发的时间复杂度是O(n^3),对于5000的数据,显然TLE
首先枚举k是肯定的,
对于每个k,把序列扫一遍得到答案,到达n^2,才可满足时间要求,所以要思考一下对于区间如何处理
考虑到反转区间后,区间内部不变的是相邻元素之间的关系,所以可以另t[n]表示该元素与前面的元素的关系,相同的话就用t[i]=0,否则t[i]=1,
对于区间[a,a+k-1],反转后变化的是t[a]和t[a+k]
所以对于一个t[i]=1,只需要改变t[i]和t[i+k]
这样就可以在O(n)的时间内对一个k求解
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #include<string> 5 #include<queue> 6 #include<vector> 7 #include<set> 8 #include<map> 9 #include<algorithm> 10 11 #define ll long long 12 #define mem(a,b) memset(a,b,sizeof(a)) 13 #define now t 14 const int maxn=6000; 15 int ini[maxn]; 16 char data[maxn]; 17 int t[maxn]; 18 using namespace std; 19 int ans=9999999,K; 20 int main() 21 { 22 int n;scanf("%d",&n); 23 data[0]='F'; 24 //scanf("%s",data+1); 25 for(int i=1;i<=n;i++)//预处理 26 { 29 cin>>data[i]; 30 if(data[i]==data[i-1]) ini[i]=0; 31 else ini[i]=1; 32 } 33 for(int k=1;k<=n;k++) 34 { 35 memcpy(t,ini,sizeof(ini)); 36 int cnt=0; 37 for(int i=1;i<=n-k+1;i++) 38 { 39 if(t[i]){cnt++;t[i+k]^=1;}//t[i]不会再用到了,不需要再改动了 40 } 41 for(int i=n-k+2;i<=n;i++)//后面这些元素不够一个区间k的数量 42 { 43 if(t[i]){cnt=999999;break;} 44 } 45 if(cnt<ans){ans=cnt;K=k;} 46 } 47 printf("%d %d ",K,ans); 48 }