首先考虑最左端的牛。包含这头牛的区间只有一个,因此如果这头牛面朝前方,这个区间不反转,面朝后方则反转。以此类推,逐渐缩小问题规模。
用数组j[i]=1代表区间[i,i+K-1]进行了反转 j[i]=0代表不反转。
如果一头牛之前被反转的次数为奇数,则朝向和刚开始相反,为偶数则相同。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<iostream> #include<memory.h> using namespace std; int N,dir[5005]; int j[5005]; //标记区间[i,i-K+1]是否反转 反转则为1, 不反转为0 int calc(int K) { memset(j,0,sizeof(j)); int res=0; //反转次数 int sum=0; //j的和 用来表示当前块之前所经历的反转次数 for(int i=0;i+K<=N;i++) { //计算区间[i,i-K+1] if((dir[i]+sum)%2!=0)//需要进行反转 { j[i]=1; res++; } sum+=j[i]; if(i-K+1>=0) { //保持区间长度 sum-=j[i-K+1]; } } //检查剩下的K-1头牛是否有朝后的情况 for(int i=N-K+1;i<N;i++) { //需要进行反转 无解 if((dir[i]+sum)%2!=0) { return -1; } if(i-K+1>=0) { //保持区间长度 sum-=j[i-K+1]; } } return res; } int main() { cin>>N; char c; for(int i=0;i<N;i++) { //牛的方向 0:F 1:B cin>>c; if(c=='F') dir[i]=0; else dir[i]=1; } //最少操作次数 与之对应的最小操作长度 int M=N,K=1; for(int i=1;i<=N;i++) { int x=calc(i); if(x>0&&x<M) { K=i; M=x; } } cout<<K<<" "<<M<<endl; return 0; }
先指定好第一行的翻转方法。此时能够翻转(1,1)的就只剩下(2,1)了,可以直接判断(2,1)是否需要翻转。
类似的(2,1)~(2,N)都能这样判断,如此反复下去就可以确定所有各自的翻转方法。
最后(M,1)~(M,N)如果并非全为白色,就意味着不存在可行的操作方法。
我们需要枚举第一行的所有翻转方法。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<iostream> #include<memory.h> using namespace std; const int dx[5]= {-1,0,0,0,1}; const int dy[5]= {0,-1,0,1,0}; int M,N; int tile[16][16]; //起始 int opt[16][16]; //保存最优解 int flip[16][16]; //保存中间结果 //查询(x,y)的颜色 int get(int x,int y) { int c=tile[x][y]; for(int i=0; i<5; i++) { int x2=x+dx[i]; int y2=y+dy[i]; if(x2>=0&&y2>=0&&x2<M&&y2<N) { c+=flip[x2][y2]; } } return c%2; } //求出第一行确定情况下的最小操作次数 //不存在解的话返回-1 int calc() { //求出从第二行开始的翻转方法 for(int i=1; i<M; i++) { for(int j=0; j<N; j++) { if(get(i-1,j)!=0) { //(i-1,j)是黑色的话 必须翻转这个格子 flip[i][j]=1; } } } //判断最后一行是否全白 for(int j=0; j<N; j++) { if(get(M-1,j)!=0) { return -1; //无解 } } //统计翻转的次数 int res=0; for(int i=0; i<M; i++) { for(int j=0; j<N; j++) { res+=flip[i][j]; } } return res; } int main() { cin>>M>>N; for(int i=0; i<M; i++) { for(int j=0; j<N; j++) { cin>>tile[i][j]; } } int res=-1; //按照字典序尝试第一行的所有可能性 for(int i=0; i<1<<N; i++) { memset(flip,0,sizeof(flip)); for(int j=0; j<N; j++) { flip[0][N-j-1]=i>>j&1; } int num=calc(); if(num>=0&&(res<0||res>num)) { res=num; memcpy(opt,flip,sizeof(flip)); } } if(res<0) { //无解 cout<<"IMPOSSIBLE"<<endl; } else { for(int i=0; i<M; i++) { for(int j=0; j<N; j++) { cout<<opt[i][j]<<" "; } cout<<endl; } } return 0; }