Description:
有一个n*m的格子,每个格子都有黑白两面(0表示白色,1表示黑色)。我们需要把所有的格子都反转成黑色,每反转一个格子,它上下左右的格子都会跟着反转。请求出用最小步数完成反转时每个格子反转的次数。有多个解时,输出字典序最小的一组。
Analysis:
一个格子没有必要翻转两次。
如果第一行翻转方法确定了,那么递推下面所有行就都确定了。
先确定第一行翻转方法O(2^n),接下来每一行,如果某一个格子的上一行格子是黑色的,那么就翻转这个格子。
时间复杂度O(nm2^n)
Code
#include<cstdio>
#include<algorithm>
#include<cstring>
#define White 0
#define Black 1
using namespace std;
const int N = 20;
const int dx[5] = {-1,0,0,0,1};
const int dy[5] = {0,-1,0,1,0};
int tile[N][N],flip[N][N],opt[N][N];
int m,n;
int getColour(int x,int y)
{
int c = tile[x][y];
for(int d = 0;d < 5;++d)
{
int x1 = x + dx[d],y1 = y + dy[d];
if((0 <= x1 && x1 < m) && (0 <= y1 && y1 < n))
{
c += flip[x1][y1];
}
}
return c % 2;
}
int calc()
{
for(int i = 1;i < m;++i)
{
for(int j = 0;j < n;++j)
{
if(getColour(i-1,j) == Black)
{
flip[i][j] = 1;
}
}
}
for(int i = 0;i < n;++i)
{
if(getColour(m - 1,i) == Black)
{
return -1;
}
}
int ans = 0;
for(int i = 0;i < m;++i)
{
for(int j = 0;j < n;++j)
{
ans += flip[i][j];
}
}
return ans;
}
void solve()
{
int ans = 10000;
int flag = 1;
for(int i = 0;i < (1 << n);++i)
{
memset(flip,0,sizeof(flip));
for(int j = 0;j < n;++j)
{
flip[0][n - j - 1] = (i >> j) & 1;
}
int num = calc();
if(num < ans && num > 0)
{
ans = num;
flag = 0;
memcpy(opt,flip,sizeof(flip));
}
}
if(flag)
{
printf("IMPOSSIBLE
");
}
else{
for(int i = 0;i < m;++i)
{
for(int j = 0;j < n;++j)
{
printf("%d%c",opt[i][j],j + 1 == n ? '
' : ' ');
}
}
}
}
int main()
{
scanf("%d%d",&m,&n);
for(int i = 0;i < m;++i)
{
for(int j = 0;j < n;++j)
{
scanf("%d",&tile[i][j]);
}
}
solve();
return 0;
}