这个是个开关问题,也是个dfs,我一开始看主流题解没看懂怎么回事,后来看了一个写的比较好的才看懂,这题还是挺难的,感觉
主要有几点1:用二进制来模拟那个第一行,1000就是1<<3,然后就是吧
2:如果第一行定下来了,那么下面的就都定下来了,为啥,因为上一行的数,可以用下一行对应的数来翻掉,所以就是说,确定了第一行就行了,然后找这个最佳的翻牌子方法,最后输出,就可以了
#include<cstdio>
#include<cstring>
using namespace std;
int map[20][20] , vis[20][20] , t[20][20];//vis翻转次数,t中间状态
int d[4][2] = {{0,-1},{1,0},{-1,0},{0,1}};
int cnt , N ,M;
void Flip(int i , int j)//翻转i,j
{
cnt++;//次数加1
t[i][j] = !t[i][j];//先翻自己
vis[i][j] = 1;
for(int k = 0 ; k < 4 ; k++){
int x = i + d[k][0];
int y = j + d[k][1];
if(x < 0 || y < 0 || x >= N || y >= M)
continue;
t[x][y] = !t[x][y];//.符合条件就要翻
}
}
bool judge(int num)//判断状态是否合理
{
cnt = 0;
memcpy(t , map , sizeof(t));//给状态
for(int i = 0 ; i < M ; i++){
if(num & (1<<i)){//如果某一位为1,那么就翻这个位置
Flip(0 , i);
}
}
for(int i = 0 ; i < N - 1 ; i++){
for(int j = 0 ; j < M ; j++){
if(t[i][j] == 1)//依次,如果上一个是1,那么下一行对应的就该翻了
Flip(i + 1 , j);
}
}
for(int i = 0 ; i < M ; i++)
if(t[N-1][i]) return false;//但是最后一行没有下一行来给他翻来转移,所以最后一行一定是0
return true;
}
int main()
{
int i , j;
while(~scanf("%d %d", &N , &M)){
for(int i = 0 ; i < N ; i++){
for(int j = 0 ; j < M ; j++)
scanf("%d",&map[i][j]);
}
int step = N * M + 1;
int p = -1;
for(int i = 0 ; i < (1 << M ) ;i++){
if(judge(i) && cnt < step){//枚举第一行
step = cnt;
p = i;
}
}
memset(vis , 0 , sizeof(vis));
if(p >= 0){//找个最小的状态,然后改变
judge(p);
for(int i = 0 ; i < N ; i++){
for(int j = 0 ; j < M ; j++)
if(j == M - 1) printf("%d\n",vis[i][j]);
else printf("%d ",vis[i][j]);
}
}
else printf("IMPOSSIBLE\n");
}
return 0;
}