#include <iostream>
#include <cstring>
using namespace std;
// 邻接格子的坐标
const int dx[5] = {-1, 0, 0, 0, 1};
const int dy[5] = {0, -1, 0, 1, 0};
const int MAX_M = 16;
const int MAX_N = 16;
int M, N;
int tile[MAX_M][MAX_N];
int opt[MAX_M][MAX_N]; // 保存最优解
int flip [MAX_M][MAX_N]; // 保存中间结果
// 查询(x, y)的颜色
int get(int x, int y)
{
int c = tile[x][y];
for (int d = 0; d < 5; d++)
{
int x2 = x + dx[d], y2 = y + dy[d];
if (x2 >= 0 && x2 < M && y2 >= 0 && y2 < N)
{
c += flip[x2][y2];
}
}
return c % 2;
}
// 求出第1行确定情况下的最小操作次数
int calc()
{
// 求出从第2行开始的翻转方法
for (int i = 1; i < M; i++)
for (int j = 0; j < N; j++)
{
if (get(i - 1, j) != 0)
{
// 如果同列的上一行为黑色,则必须翻转,因为此后,只有该点的翻转可以改变正上方的棋子颜色了
flip[i][j] = 1;
}
}
// 判断最后一行是否空白
for (int j = 0; j < N; j++)
if (get(M - 1, j)) // 无解
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;
}
void solve()
{
int res = -1;
memset(opt, 0, sizeof(opt));
// 按照字典序开始尝试第一行所有的可能性
for (int i = 0; i < 1 << N; i++)
{
memset(flip, 0, sizeof(flip)); // 每次必须重新清空flip
for (int j = 0; j < N; j++)
{
flip[0][N - 1 - j] = i >> j & 1;
}
int num = calc();
if (num >= 0 && (res == -1 || res > num))
{
res = num;
memcpy(opt, flip, sizeof(flip));
}
}
if (res == -1) cout << "IMPOSSIBLE" << endl;
else
{
for (int i = 0; i < M; i++)
{
for (int j = 0; j < N - 1; j++)
cout << opt[i][j] << " ";
cout << opt[i][N - 1] << endl;
}
}
}
int main()
{
while (cin >> M >> N)
{
for (int i = 0; i < M; i++)
for (int j = 0; j < N; j++)
cin >> tile[i][j];
solve();
}
return 0;
}
poj3279 Fliptile
最新推荐文章于 2021-07-06 00:06:22 发布