题目:http://poj.org/problem?id=3279
给出一个m*n的矩形板块,每一个小栅格有白色和黑色两面,可以对其进行反转,不过牛蹄很大,反转一个栅格同时也会反转周围的栅格,问是否能将版块上所有栅格的数字都变为白色,若能,输出翻动最小次数时反转了哪些栅格,若不能,输出“IMPOSSIBLE”。
#include<iostream>
#include<algorithm>
#include<cstring>
#include<string>
#include<cstdio>
using namespace std;
const int INF = 0x3f3f3f3f;
const int MAXN = 30;
int m, n;
int min_cnt = INF;
bool has = false;
int Map[MAXN][MAXN]; // 保存原图
int res[MAXN][MAXN]; // 保存最终结果
int tmp[MAXN][MAXN]; // 保存中间结果
int g[MAXN][MAXN]; // 当前地图
int solve(int st)
{
int cnt = 0;
for(int i = 0; i < n; i++) // 第0行
{
if(st & (1 << i)) // st的第i位是1,则翻转第0行第i位的栅格
{
tmp[0][i] = 1; // 记录该位置翻转了多少次,(0, i)位置翻转1次,
cnt ++; // 总次数+1
// 对周围栅格的影响:
g[0][i] ^= 1; // 当前地图,(0,i)位置若原来为0,则异或后(翻转)变为1,反之
if(i - 1 >= 0) g[0][i - 1] ^= 1;
if(i +1 < n) g[0][i + 1] ^= 1;
if(m > 1) g[1][i] ^= 1;
}
}
// 第0行的翻转结束
for(int i = 1; i < m; i++) // 从第1行开始往下翻转
{
for(int j = 0; j < n; j++)
{
if(g[i - 1][j])
{
tmp[i][j] = 1;
cnt++;
g[i - 1][j] = 0;
g[i][j] ^= 1;
if(j - 1 >= 0) g[i][j - 1] ^= 1;
if(j + 1 < n) g[i][j + 1] ^= 1;
if(i + 1 < m) g[i + 1][j] ^= 1;
}
}
}
for(int i = 0; i < n; i++)
{
if(g[m - 1][i]) return INF;
}
return cnt;
}
int main()
{
cin >> m >> n;
memset(Map, 0, sizeof(Map));
for(int i = 0; i < m; i++)
for(int j = 0; j < n; j++)
cin >> Map[i][j];
for(int k = 0; k <= (1 << n) ; k++) // 枚举第0行的所有可能翻转,如0100代表只翻转第二个位置
{
memset(tmp, 0, sizeof(tmp));
memcpy(g, Map, sizeof(Map)); // 复制一遍原图到 g[][]
int cnt = solve(k);
if(cnt != INF)
{
has = true;
if(cnt < min_cnt) // 找到一种翻转次数更少的方式
{
min_cnt = cnt;
memset(res, 0, sizeof(res));
memcpy(res, tmp, sizeof(tmp)); // 将翻转情况保存到res数组
}
}
}
if(has)
{
for(int i = 0; i < m; i++)
{
for(int j = 0; j < n; j++)
{
cout << res[i][j] << " ";
}
cout << endl;
}
}
else
{
cout << "IMPOSSIBLE" << endl;
}
return 0;
}