学位运算的时候以为用不上看了几眼直接就跳过了……结果这次吃亏了……在老师的指导下第一次学会了位运算,留念~具体见代码注释~
11464 - Even Parity
Time limit: 3.000 seconds
D | Even Parity Input: Standard Input Output: Standard Output |
We have a grid of size N x N. Each cell of the grid initially contains a zero(0) or a one(1).
The parity of a cell is the number of 1s surrounding that cell. A cell is surrounded by at most 4 cells (top, bottom, left, right).
Suppose we have a grid of size 4 x 4:
1 | 0 | 1 | 0 | The parity of each cell would be | 1 | 3 | 1 | 2 |
1 | 1 | 1 | 1 | 2 | 3 | 3 | 1 | |
0 | 1 | 0 | 0 | 2 | 1 | 2 | 1 | |
0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
For this problem, you have to change some of the 0s to 1s so that the parity of every cell becomes even. We are interested in the minimum number of transformations of 0 to 1 that is needed to achieve the desired requirement.
Input
The first line of input is an integer T (T<30) that indicates the number of test cases. Each case starts with a positive integer N(1≤N≤15). Each of the next N lines contain N integers (0/1) each. The integers are separated by a single space character.
Output
For each case, output the case number followed by the minimum number of transformations required. If it's impossible to achieve the desired result, then output -1 instead.
Sample Input Output for Sample Input
3 3 0 0 0 0 0 0 0 0 0 3 0 0 0 1 0 0 0 0 0 3 1 1 1 1 1 1 0 0 0 | Case 1: 0 |
Problem Setter: Sohel Hafiz,
Special Thanks: Derek Kisman, Md. Arifuzzaman Arif
//最容易想到的算法就是,枚举每个数字“变”还是“不变”,但运算量太大
//但是注意到n只有15,第一行最多有2^15=32768种可能,一下每行都可以递推解出,可行
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 15+5;
const int INF = 1000000000;//极大的数,天文数字,表示不可能
int n, A[maxn][maxn], B[maxn][maxn];//养成习惯,大数组外部定义
//本程序的亮点:
//利用数据s的二进制来代表01矩阵的第一行,枚举数据s由0到全1的变化,表示B矩阵第1行的所有可能
//前提:
//矩阵的大小n比整数的二进制位数小,且为01矩阵
int check(int s)
{
memset(B, 0, sizeof(B));
for(int c = 0; c < n; c++)
{
if(s & (1 << c)) B[0][c] = 1;// s & (1 << c) :表示 s 的二进制第c位是否为1
else if(A[0][c] == 1) return INF;//1不能变成0
}
//已知n<16故矩阵每行可表示为:
// 001100...0001(类似,不超过16位的二进制)
//因此,第1行的可选情形有:
// 000000...0000
// 000000...0001
// 000000...0010
// ...
// 111111...1111
//一旦确定第1行,则可确定第2行,第三行...
//把二进制的数据s如何输入矩阵B呢?
//如果s的二进制c位为1,则B[0][c]=1;
// if(s & (1 << c)) B[0][c] = 1;
//若A[0][c]==1,表示没有改变,否则相当于把A[0][c]的0变为1得到B[0][c](0->1)
//反之,若s & (1 << c)为0且A[0][c]为1,如果把A[0][c]==1变为B[0][c]==0,规则不允许(1->0),故返回不可能,即返回很大的数(天文数字)
//else if(A[0][c] == 1) return INF;
//好了,第1行B[0][c]确定之后,可推算第2行的0->1改变数,第3行的0->1改变数...
//通过第1行的变化(枚举000000...0000到111111...1111)来实现
for(int r = 1; r < n; r++)
for(int c = 0; c < n; c++)
{
int sum = 0;//元素B[r-1][c]的上,左,右3个元素之和
//避免对边角元素单独讨论
if(r > 1) sum += B[r-2][c];//左
if(c > 0) sum += B[r-1][c-1];//上
if(c < n-1) sum += B[r-1][c+1];//右
B[r][c] = sum % 2;//偶:0;奇:1.生成下一列
if(A[r][c] == 1 && B[r][c] == 0) return INF;//1不能变成0
}
int cnt =0;
for(int r = 0; r < n; r++)
for(int c = 0; c < n; c++)
if(A[r][c] != B[r][c]) cnt++;
return cnt;
}
int main()
{
int T;
scanf("%d", &T);
for(int kase = 1; kase <= T; kase ++)
{
scanf("%d", &n);
for(int r = 0; r < n; r++)
for(int c = 0; c < n; c++)
scanf("%d", &A[r][c]);
int ans = INF;
for(int s = 0; s < (1 << n); s++)
ans = min(ans, check(s));
if(ans == INF) ans = -1;
printf("Case %d: %d\n", kase, ans);
}
return 0;
}