uva 11464
分类: 枚举 和 剪枝
题意: 给定矩阵由0,1组成,使用最少的改变(使0变成1),使得任何元素的上下左右元素(如果存在)和为偶数
输入: 测试组数, 矩阵大小n ,矩阵每一个数
输出: 最小翻转次数,无法实现则输出-1
解法:
类似关灯问题,枚举第一行的状态(2^n)种情况,依题意,所有的后续方格可以由上面确定,检查翻转次数与是否满足
关键在于如何编写,即按照顺序确定每一个方格的状态
在代码中体现为 check函数,注意实现过程中适当剪枝,提高速度
#include <iostream> #include <vector> #include <map> #include <list> #include <set> #include <deque> #include <stack> #include <queue> #include <algorithm> #include <cmath> #include <cctype> #include <cstdio> #include <iomanip> #include <cmath> #include <cstdio> #include <iostream> #include <string> #include <sstream> #include <cstring> #include <queue> using namespace std; ///宏定义 const int INF = 990000000; const int MAXN = 30; const int maxn = MAXN; ///全局变量 和 函数 int A[maxn][maxn], B[maxn][maxn]; int n; int check(int s) { memset(B, 0, sizeof(B)); int cnt = 0; for (int i = 0; i < n; i++) { if (s & (1 << i)) B[0][i] = 1; else if (A[0][i] == 1) return INF; } for (int i = 1; i < n; i++) { for (int j = 0; j < n; j++) { int sum = 0; if (i > 1) sum += B[i - 2][j]; if (j > 0) //注意边界取值 sum += B[i - 1][j - 1]; if (j < n - 1) //注意边界取值 sum += B[i - 1][j + 1]; B[i][j] = sum % 2; if (B[i][j] == 0 && A[i][j] == 1) return INF; } } for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { if (A[i][j] != B[i][j]) cnt++; } } return cnt; } int main() { ///变量定义 int T; int cases = 1; scanf("%d", &T); while (T--) { memset(A, 0, sizeof(A)); scanf("%d", &n); for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { scanf("%d", &A[i][j]); } } //枚举第一行 int ans = INF; for (int i = 0; i < (1 << n); i++) { int cnt = check(i); ans = min(ans, cnt); } if (ans == INF) ans = -1; printf("Case %d: %d\n", cases++, ans); } ///结束 return 0; }