POJ-3279 经典翻转问题

题目大意:给定一个矩阵,要么黑要么白,求出按照规则将其翻转为全白最小翻转次数的方案,如有多种最小方案则输出字典序最小的方案

分析:此题为经典的翻转问题(开关问题),在看此题解析之前,希望大家看看我这篇文章:点击打开链接

附上代码:

#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;
	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;
		int t = solve();
		if (t < ans)
		{
			ans = t;
			memcpy(c, b, sizeof b);
		}
	}
	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;
}



你可能感兴趣的:(ACM,题解报告,翻转问题)