poj 3279

来源:互联网 发布:手机贴吧抢二楼软件 编辑:程序博客网 时间:2024/06/06 03:13

提前声明,代码是我偷来的...博主是这位:http://blog.csdn.net/ac_hell/article/details/51077271

代码很简练,至少在我从百度上找的代码来看是这样

作为一个acm萌新,我相信很多人就算找到了代码也看不懂为什么这么写(我也是看了一天才看懂流程),所以我就在原博主代码的基础上做一些注释

#include<cstdio>
#include<cstring>
using namespace std;
#define INF 0x3f3f3f3f
bool a[20][20];   //存图
bool b[20][20];   //保存当前翻转方案
bool c[20][20];   //保存最优解
int d[5][2] = { { -1, 0 }, { 1, 0 }, { 0, 0 }, { 0, -1 }, { 0, 1 } };
int ans = INF;
int n, m;
bool getcolor(int x, int y)    //得到x,y的颜色
{
int res = a[x][y];
for (int i = 0; i < 5; i++)
{
int fx = x + d[i][0], fy = y + d[i][1];
if (fx >= 1 && fx <= n && fy >= 1 && fy <= m) res += b[fx][fy];//根据当前位置的颜色和四周翻转的次数来判断当前位置是否需要翻转,其实不需要判断下面的翻转次数,因为

//我们规定除了第一行需要根据两侧翻转的情况判断自身翻转情况之外其他行都是根据上一行当前位置是黑块还是白块来决定是否翻转

}
return res % 2;
}
int solve()
{
int res = 0;
for (int i = 2; i <= n; i++)    //从第二行开始检查是否需要翻转
for (int j = 1; j <= m; j++)
if (getcolor(i - 1, j)) b[i][j] = 1;//b[0][]这一行已经进行了翻转(在main函数中memset函数下面)所以第二行的某个位置是否翻转根据第一行中对应位置是否为1进行判断

                                                                      //第二行到第n行当前位置是否反转只和上个位置有关,和当前位置是0还是1无关
for (int i = 1; i <= m; i++)     //检查最后一行是否全为白色
if (getcolor(n, i)) return INF;
for (int i = 1; i <= n; i++)    //统计翻转次数
for (int j = 1; j <= m; j++)
res += b[i][j];
return res;
}
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
scanf("%d", &a[i][j]);
for (int s = 0; s < 1 << m; s++)   //按照字典序枚举第一行所以翻转可能
{
memset(b, false, sizeof b);
for (int i = 1; i <= m; i++)
b[1][i] = s >> (m - i) & 1;//这里利用了二进制取逐次取最后一位来对b数组的第一行的每一位进行0和1两种情况的枚举
int t = solve();
if (t < ans)
{
ans = t;
memcpy(c, b, sizeof b);//如果发现了比当前ans更小的t,就把保存最优解的c数组进行更新
}
}
if (ans == INF) printf("IMPOSSIBLE\n");
else
{
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
printf("%d ", c[i][j]);
printf("\n");
}
}
return 0;
}

原创粉丝点击