UVA 11464 Even Parity(状态压缩)

题意:

给你一个 n * n 的 01 矩阵,现在你的任务是将这个矩阵中尽量少的 0 转化为 1 ,使得每个数的上下左右四个相邻的数加起来是偶数。求最少的转化个数。

解析:

首先,n 的规模并不大,最大只有15。但是完全枚举整个矩阵显然是不可能的。但是我们可以枚举第一行,然后用第一行来算出后面的所有行。

先来说下算法。对于每一行,我们通过他上面的两行来决定他的值。如果上面两行得到值为奇数,那么这一行就赋值为 1 ,否则赋值为 0 。 然后与原始矩阵比较,如果是由 1 变 0 那么这种情况是不允许的,于是继续枚举第一行,重新计算后面的。如果是由 0 变 1(或不变) 那么保存下来。最后在计算所有的格子之后,遍历一下就能统计转化数。然后取最小值就可以了。

下面用题目中给的第二组数据来演示一下如何通过上一行来计算下一行。

问题                                 正解
0 0 0                ==>             0 1 0
1 0 0                ==>             1 0 1
0 0 0                ==>             0 1 0

来举例说明。首先我们通过枚举第一行可以达到以下情况
0 1 0

如下表我们开始通过第一行来计算第二行,对于 x 所在的这一格来说,把这一格当做他上面一格的下面相邻的格子进行计算,于是对于第一行第一列来说,他上下左右相加起来为 1 + x,因为要保证是偶数,所以 x = 1。接着与原矩阵进行比较,是符合题目要求的转化(不变)。保留,继续计算下一个。

0 1 0
x

如下表开始计算第二行第二列,按照上面的方法,计算得出 0 + 0 + x 要为偶数,所以 x = 0。与原数组比较依旧是符合题目要求的。

0 1 0
1 x

如下表计算第二行第三列,同上,得出 1 + x 要为偶数,所以 x = 1。与原矩阵比较,发现是符合要求的转化(0 变 1)继续。

0 1 0
1 0 x

这样第二行就得出来了,是下面这种情况:

0 1 0
1 0 1

按照上述方法,可以计算出第三行,来完成这个例子的答案:

0 1 0
1 0 1
0 1 0

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int INF = 0x3f3f3f3f;
const int N = 18;
int grid[N][N],map[N][N];
int n;
int find(int s) {
    memset(map,0,sizeof(map));
    for(int i = 0; i < n; i++) { //将第一行转换为数组
        map[1][i+1] = (s >> i) & 1;
        if(grid[1][i+1] == 1 && map[1][i+1] == 0) { //不能将1变为0
            return INF;
        }
    }
    int sum = 0;
    for(int i = 1; i <= n-1; i++) { //枚举前1~n-1行
        for(int j = 1; j <= n; j++) {
            sum = map[i-1][j] + map[i][j+1] + map[i][j-1];
            map[i+1][j] = sum % 2;
            if(grid[i+1][j] == 1 && map[i+1][j] == 0) { //不合法
                return INF;
            }
        }
    }
    int cnt = 0;
    for(int i = 1; i <= n; i++) {
        for(int j = 1; j <= n; j++) {
            if(grid[i][j] != map[i][j]) {
                cnt++;
            }
        }
    }
    return cnt;
}
int main() {
    int T ,cas = 1;
    scanf("%d",&T);
    while(T--) {
        scanf("%d",&n);
        memset(grid,0,sizeof(grid));
        for(int i = 1; i <= n; i++) {
            for(int j = 1; j <= n; j++) {
                scanf("%d",&grid[i][j]);
            }
        }

        int ans = INF;
        printf("Case %d: ",cas++);
        for(int i = 0; i < (1 << n); i++) { //枚举2^15
            ans = min(ans,find(i));
        }
        if(ans == INF) {
            printf("%d\n",-1);
        }else {
            printf("%d\n",ans);
        }
    }
    return 0;
}

你可能感兴趣的:(uva,11464)