您的位置:首页 > 编程语言

poj3279 Fliptile

2017-08-26 09:23 148 查看
#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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息