题目链接:poj_3279 Fliptile
题目大意:
- 01矩阵,改变其中一个,周围上下左右都改变(0->1 ,1->0),目的是让这个矩阵全都变为0
- 求解改变次数最小的那个,如果最小次数有多种情况,输出字典序列最小的情况
解题思路:
- 从第一行开始枚举,直到最后一行
- 如果最后一行不全为0,那么就无解
- 考虑翻转奇数次则会改变状态:
奇数次为自身+上、左和右四个状态相加为奇数(自身状态此时还为1),则需要下一行来辅助变换
0 + 上、左、右 = 奇数, 那么此时0改变为1
1 + 上、左、右= 奇数,那么此时1还是1
代码如下:
状态压缩 + DFS
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
#define inf 0x3f3f3f3f
#define mm(a,x) memset(a,x,sizeof a)
#define ll long long
const int N = 30;
int n,m;
int g[N][N];
int tmp[N][N];
int ans[N][N];
int res;
int dx[4] = {0,-1,0,0};
int dy[4] = {0,0,-1,1};
int dfs() {
for(int i = 0; i < n - 1; i ++ ) {
for(int j = 0; j < m; j ++ ) {
int p = g[i][j];
//枚举四种情况:自身、上、左、右
for(int k = 0; k < 4; k ++ ) {
int a = i + dx[k];
int b = j + dy[k];
if(a < 0 || a > n - 2 || b < 0 || b > m - 1) continue;
p += tmp[a][b];
}
//如果为奇数
if(p & 1) {
tmp[i + 1][j] = 1;
}
}
}
int i = n - 1;
for(int j = 0; j < m; j ++ ) {
int p = g[i][j];
for(int k = 0; k < 4; k ++ ) {
int a = i + dx[k];
int b = j + dy[k];
if(a < 0 || a > n - 1 || b < 0 || b > m - 1) continue;
p += tmp[a][b];
}
if(p & 1) {
return -1;
}
}
int t = 0;
for(int i = 0; i < n; i ++ ) {
for(int j = 0; j < m; j ++ ) {
if(tmp[i][j] & 1) t ++;
}
}
return t;
}
int main() {
cin >> n >> m;
for(int i = 0; i < n; i ++ ) {
for(int j = 0; j < m; j ++ ) {
cin >> g[i][j];
}
}
res = inf;
for(int i = 0; i < (1 << n); i ++ ) {
mm(tmp,0);
//第一行
for(int j = 0; j < m; j ++ ) {
tmp[0][j] = (i >> j) & 1;
}
int t = dfs();
if(t == -1) continue;
//更新最优解
if(res > t) {
res = t;
for(int i = 0; i < n; i ++ ) {
for(int j = 0; j < m; j ++ ) {
ans[i][j] = tmp[i][j];
}
}
}
}
//如果无解
if(res == inf) {
cout<<"IMPOSSIBLE\n";
} else {
for(int i = 0; i < n; i ++ ) {
for(int j = 0; j < m; j ++ ) {
if(j) cout<<" ";
cout<<ans[i][j];
}
puts("");
}
}
return 0;
}