POJ 3279 Flip title 状态压缩+暴力

POJ 3279 Flip title 状态压缩+暴力

题意

\(M*N\) (<=15)的01矩阵,定义操作\(f[i][j]\):使第i行j列的元素自身以及上下左右共五个格子翻转,找出翻转次数最少的方案,如果有多个解,输出字典序最小的方案

题目链接

思路

N的范围只有15,构造状态数组\(r[M]\),其值为矩阵每一行的状态,比如样例输入第一行1001,就可以将其表示为1001(2) = 9。定义操作矩阵\(b[M]\),表示翻转对应位,比如0001(2) = 1,表示第1,2,3位不翻转,第四位翻转。

枚举第一行的\(2^n\)种情况,对第一行进行操作后,依次处理其他行,每次将其上面一行变为全0,处理完毕后如果最后一行也为0,则得到一个解。

显然,对于\(a[i][j]=1\), 我们只需要令\(b[i+1][j]=1\), 就能让\(a[i]\)的1被翻转成0。设第\(i\)行为\(a_i\) 则对第\(i+1\)行需要进行的操作就是\(a_i\) ,我们只要把路径最小的\(a_i\)存起来,因为我们是按照字典序枚举的,所以找到的第一个解就是\(Ans\)

以下给出用位运算压缩的解法,作为比较,数组解法的时间消耗要远大于位运算,实测中大约在3倍时间差距左右。

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int INF = 0x3f3f3f3f;
const int MAX = 15 + 5;
int n, m;
int r[MAX];    //原始矩阵
int a[MAX];    //临时矩阵
int b[MAX];    //操作矩阵
int ans[MAX];  //结果矩阵
int cnt;
void flip(int i, int j) {  //对a[i][j]翻转
    cnt++;
    a[i] ^= 1 << j;
    a[i] ^= 1 << (j - 1);
    a[i] ^= 1 << (j + 1);
    a[i + 1] ^= 1 << j;
    a[i - 1] ^= 1 << j;
}
void solve(int i, int x) {  //对a[i]进行操作x
    b[i] = x;
    for (int j = 1; j <= m; j++) {
        if (x & (1 << j)) {
            flip(i, j);
        }
    }
}
int main(void) {
    while (scanf("%d%d", &n, &m) != EOF) {
        memset(a, 0, sizeof(a));
        memset(b, 0, sizeof(b));
        memset(r, 0, sizeof(r));
        int t;
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= m; j++) {
                scanf("%d", &t);
                if (t) {
                    r[i] |= 1 << j;  //对应位置1
                }
            }
        }
        int len = INF;
        int bound = 1 << m;
        for (register int state = 0; state < bound; state++) {
            cnt = 0;
            for (int i = 1; i <= n; i++) {
                a[i] = r[i];
            }
            int i = state << 1;  //做了一个偏移
            solve(1, i);
            for (int j = 2; j <= n; j++) {
                solve(j, a[j - 1]);
            }
            int flag = (a[n] >> 1) % bound;  //去掉偏移量
            if (cnt < len && !flag) {
                len = cnt;
                for (int k = 1; k <= n; k++) {
                    ans[k] = b[k];
                }
            }
        }
        if (len == INF) {
            printf("IMPOSSIBLE\n");
        } else {
            for (int i = 1; i <= n; i++) {
                printf("%d", ans[i] & (1 << 1) ? 1 : 0);
                for (int j = 2; j <= m; j++) {
                    printf(" %d", ans[i] & (1 << j) ? 1 : 0);
                }
                printf("\n");
            }
        }
    }
    return 0;
}

posted @ 2020-03-24 20:21  HermitG  阅读(110)  评论(0编辑  收藏  举报