POJ 3276 Face The Right Way 翻转(开关问题)
题目:Click here
题意:n头牛排成一列,F表示牛面朝前方,B表示面朝后方,每次转向K头连续的牛的朝向,求让所有的牛都能面向前方需要的最少的操作次数M和对应的最小的K。
分析:一个区间反转偶数次等于没反转,f[i]表示区间[i,i+K-1]是(1)否(0)进行了反转,所以在考虑第i头牛的时候,如果f[i-K+1]+f[i-K]+···+f[i-1]为奇数的话,这头牛的方向与起始的方向是相反的,否则方向没变。
1 #include <cstdio> 2 #include <cstring> 3 using namespace std; 4 const int M = 5e3+3; 5 int n; 6 int dir[M]; // 牛的方向(0:F,1:B) 7 int f[M]; // 区间[i,i-K+1]是否进行了反转 8 9 void input() { 10 while( ~scanf("%d\n", &n ) ) { 11 for( int i=0; i<n; i++ ) { 12 char x; 13 scanf("%c\n", &x ); 14 if( x == 'F' ) dir[i] = 0; 15 else dir[i] = 1; 16 } 17 } 18 } 19 int calc( int k ) { // 对于固定的K,求对应的最小的操作数,无解为-1 20 memset( f, 0, sizeof(f) ); 21 int sum = 0; 22 int ret = 0; // f的部分和 23 for( int i=0; i+k<=n; i++ ) { 24 if( (dir[i]+sum)%2 != 0 ) { // 当前牛面朝后方 25 ret++; 26 f[i] = 1; 27 } 28 sum += f[i]; 29 if( i-k+1 >= 0 ) 30 sum -= f[i-k+1]; 31 } 32 for( int i=n-k+1; i<n; i++ ) { // 检查剩下的牛的朝向 33 if( (dir[i]+sum)%2 != 0 ) 34 return -1; 35 if( i-k+1 >= 0 ) 36 sum -= f[i-k+1]; 37 } 38 return ret; 39 } 40 void solve() { 41 int K = 1, M = n; 42 for( int i=1; i<=n; i++ ) { 43 int m = calc( i ); 44 if( m < M && m >= 0 ) { 45 M = m; 46 K = i; 47 } 48 } 49 printf("%d %d\n", K, M ); 50 } 51 int main() { 52 input(); 53 solve(); 54 return 0; 55 }