题目大意
解题思路
先说普通的爆搜,对于每个格子,要么翻转要么不翻转,因此复杂度为
O
(
2
M
N
)
O(2^{MN})
O(2MN).
转换思路:
- (1): 首先对第一排的所有翻转情况进行枚举:共 O ( 2 N ) O(2^N) O(2N)
- (2): 在枚举的每一种情况下,下一排的格子的翻转情况被上一排的格子颜色唯一确定。比如当前扫描到位置 ( i , j ) (i,j) (i,j),其翻转情况由位置 ( i − 1 , j ) (i-1, j) (i−1,j)的小格颜色唯一确定。
- (3): 最后只需要判断最后一排是否全满足情况,满足即可解,不满足即不可解。
- (4): 复杂度 O ( M N 2 N ) O(MN2^N) O(MN2N)
技巧:
- 采用位运算对第一排每一种情况进行模拟。
代码
#include<iostream>
#include<cstring>
using namespace std;
const int MAX = 17;
int M, N;
int base[MAX][MAX];
int flip[MAX][MAX];
int ans_flip[MAX][MAX];
int dir[4][2] = {{-1,0},{0,1},{0,0},{0,-1}};
int is_black(int x, int y)
{
int flip_num = 0;
for(int i=0; i<4; i++)
{
int nx = x+dir[i][0];
int ny = y+dir[i][1];
if(nx < 0 || nx >= M || ny < 0 || ny >= N)
continue;
flip_num += flip[x+dir[i][0]][y+dir[i][1]];
}
if((flip_num+base[x][y])%2)
return 1;
return 0;
}
int main()
{
cin >> M >> N;
for(int i=0; i<M; i++)
for(int j=0; j<N; j++)
cin >> base[i][j];
int ans = MAX*MAX;
for(int i=0; i<(1<<N); i++)
{
memset(flip, 0, sizeof(flip));
int state = i;
for(int j=N-1; j>=0; j--)
{
flip[0][j] = state&1;
state>>=1;
}
for(int m=1; m<M; m++)
for(int n=0; n<N; n++)
if(is_black(m-1, n))
flip[m][n] = 1;
int flag = 1;
for(int n=0; n<N; n++)
{
if(is_black(M-1, n))
{
flag = 0;
break;
}
}
if(flag)
{
int num = 0;
for(int i=0; i<M; i++)
for(int j=0; j<N; j++)
num += flip[i][j];
if(num < ans)
{
for(int i=0; i<M; i++)
for(int j=0; j<N; j++)
ans_flip[i][j] = flip[i][j];
ans = num;
}
}
}
if(ans == MAX * MAX)
cout << "IMPOSSIBLE" << endl;
else
{
for(int i=0; i<M; i++)
{
for(int j=0; j<N; j++)
{
if(j)
cout << " " << ans_flip[i][j];
else
cout << ans_flip[i][j];
}
cout << endl;
}
}
return 0;
}