题意:n头牛站成线,有朝前有朝后的的,然后每次可以选择大小为k的区间里的牛全部转向,会有一个最小操作m次使得它们全部面朝前方。问:求最小操作m,再此基础上求k。
题解:1、5000头牛不是小数目,再怎么也得要n^2的算法,其中,枚举k是需要的,这就有n了,只能想办法给出一个n在O(n)时间内求出最小次数了。
2、对于给定的k,要想O(n)内把次数算出来,即只能扫一遍,一想到的必定是从前往后扫,遇到面朝后的就转头,但这一转牵扯太多,要改太多东西,k一大直接崩溃。
3、对于每次扫描到的第i个点,都至多只能改一次才能保证效率,即只改变化的。将牛的朝向弄成依赖型,即后者依赖于前者,这样在一个区间内[a,b]翻转时,实际上[a+1,b]的依赖关系是没有改变的,改变的只有a,b+1。
4、综上,设置一种关系表示每头牛与前一头牛的朝向,最简单的就是同向与反向的差异,不妨令同向为0,反向为1,为了使得最后都朝前,可以令一头虚拟牛(即0号牛)头朝前,然后第一头牛依赖于它。
5、因此,每次检查时,只需要更改a和a+k位置的牛的依赖关系便可以解决了,最后在检查一下剩余的牛是否全是0就结束了。
#include <cstdio> #include <iostream> #include <cmath> #include <memory.h> #include <cstring> using namespace std; int N; char tmp, tmp1; int arr[5002]; int arr1[5002]; int K,M; int test(int k) { memcpy(arr1,arr,sizeof(arr)); int time = 0; for(int i=1; i<=N-k+1; i++) { if(arr1[i] == 1) { arr1[i] = 0; arr1[i+k] = abs(arr1[i+k]-1); time++; } } for(int i=N-k+1; i<=N; i++) { if(arr1[i] == 1) return 0; } return time; } void caculate() { K = 1; M = N; int tmpM; for(int i=1; i<=N; i++) { tmpM = test(i); if(tmpM>0 && tmpM<M) { M = tmpM; K = i; } } } int main(int argc, char** argv) { //freopen("E:/sample_input.txt", "r", stdin); while(scanf("%d",&N)!=EOF) { memset(arr, 0, sizeof(arr)); tmp1 = 'F'; for(int i=1; i<=N; i++) { scanf(" %c", &tmp); if(tmp == tmp1) arr[i] = 0; else arr[i] = 1; tmp1 = tmp; } caculate(); cout << K << " " << M << endl; } return 0; }