UVA 11464-Even Parity(偶数矩阵-开关问题)

11464 - Even Parity

Time limit: 3.000 seconds

We have a grid of size N × N. Each cell of the grid initially contains a zero(0) or a one(1). The parityof 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 × 4:


For this problem, you have to change some of the 0s to 1s so that the parity of every cell becomeseven. We are interested in the minimum number of transformations of 0 to 1 that is needed to achievethe desired requirement.

Input

The first line of input is an integer T (T < 30) that indicates the number of test cases. Each case startswith a positive integer N (1 ≤ N ≤ 15). Each of the next N lines contain N integers (0/1) each. Theintegers 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

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

Sample Output

Case 1: 0

Case 2: 3

Case 3: -1

题目意思:

有一个N*N的01矩阵,你的任务是把尽量少的0变成1,使得每个元素的上下左右的元素(若存在)均为偶数。

解题思路:

Note:0可以变成1,但是1不能变成0

N最大是15,我们只需要枚举第一行,每个数只能为0/1,所以最多2^15个,剩下的每一行都可以根据第一行来确定。

求解过程中用一个新的矩阵保存我们找到的解,与原矩阵相比较,计算出改变的元素个数。

枚举过程中:

①状态压缩

if(s & (1<<i)) m[0][i]=1;//判断该位是否为1
因为数最大范围是000~111共15种,所以可以分别对应枚举第一行元素值的所有情况。

按位与&运算(只有全1才为1)判断该位是否为1,如果是1,则新矩阵该位置1;如果原来的矩阵中该位为1,则无法求解,函数直接返回返回无解;

1不能变成0

 if(f[i][j]==1&&m[i][j]==0) return INF;//别忘了这句,1不能转换成0
注意特判这种无法转换的情况,无法继续求解,函数直接返回返回无解。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <bitset>
#include <algorithm>
using namespace std;
#define INF 0x3f3f3f3f
#define maxn 16
int m[maxn][maxn],f[maxn][maxn];
int n;
int valid(int s)
{
    memset(m,0,sizeof(m));
    int cnt=0;
    for(int i=0; i<n; ++i)
    {
        if(s & (1<<i)) m[0][i]=1;
        else if(f[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+=m[i-2][j];
            if(j>0)//左
                sum+=m[i-1][j-1];
            if(j+1<n)//右
                sum+=m[i-1][j+1];
            m[i][j]=sum%2;
            if(f[i][j]==1&&m[i][j]==0) return INF;//别忘了这句,1不能转换成0
        }
    for(int i=0; i<n; ++i)
        for(int j=0; j<n; ++j)
            if(m[i][j]!=f[i][j]) ++cnt;
    return cnt;
}
int main()
{
    int t,ca=0;
    scanf("%d",&t);
    while(t--)
    {
        int ans=INF;
        scanf("%d",&n);
        for(int i=0; i<n; ++i)
            for(int j=0; j<n; ++j)
                scanf("%d",&f[i][j]);
        for(int i=0; i<(1<<n); ++i)
            ans=min(ans,valid(i));
        if(ans==INF) ans=-1;
        printf("Case %d: %d\n",++ca,ans);
    }
    return 0;
}
/*
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

*/


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值