[枚举] POJ 3279

看得我一脸懵逼。。。bfs感觉用不了,dfs又想不出来

查了一下发现所有的结果都和第一行有关(怎么想出来的orz)

所以枚举第一行所有情况


一堆二进制的操作只是优化

Fliptile POJ3279 二进制压缩枚举 解题报告


#include <cstring>
#include <iostream>
#include <queue>

using namespace std;

const int N = 16;

//输入的矩阵,复制出来做翻转的矩阵,储存被翻转的位置的矩阵
int g[ N ][ N ], t[ N ][ N ], f[ N ][ N ];
int cnt, n, m;

//四个方向
int x[ 4 ] = {0, 0, -1, 1};
int y[ 4 ] = {-1, 1, 0, 0};

void flip ( int i, int j ) {
        ++cnt, f[ i ][ j ] = 1;     //步数++,记录翻转点
        t[ i ][ j ] = !t[ i ][ j ]; //翻转自己
        for ( int k = 0; k < 4; ++k )
                if ( i + x[ k ] > -1 && j + y[ k ] > -1 )
                        t[ i + x[ k ] ][ j + y[ k ] ] ^= 1; //异或翻转
}

bool ok ( int k ) {
        //初始化
        cnt = 0;
        memcpy ( t, g, sizeof ( t ) );

        // 第一行的m列,
        for ( int j = 0; j < m; ++j )
                if ( k & ( 1 << ( m - 1 - j ) ) ) //找到k中翻转的列
                        flip ( 0, j );

        for ( int i = 1; i < n; ++i ) // 把2--n行上面一行的1全翻成0
                for ( int j = 0; j < m; ++j )
                        if ( t[ i - 1 ][ j ] )
                                flip ( i, j );

        for ( int j = 0; j < m; j++ ) // 考察最后一行能不能翻成功
                if ( t[ n - 1 ][ j ] )
                        return false;

        return true;
}

int main () {
        int ans, p;

        while ( ~scanf ( "%d%d", &n, &m ) ) {
                for ( int i = 0; i < n; ++i )
                        for ( int j = 0; j < m; ++j )
                                scanf ( "%d", &g[ i ][ j ] );

                ans = n * m + 1, p = -1;

                for ( int i = 0; i < ( 1 << m ); ++i ) // 枚举 2^m 种对第一行的翻转
                        if ( ok ( i ) && cnt < ans )   // 记录最小的翻转次数
                                ans = cnt, p = i;

                memset ( f, 0, sizeof ( f ) );
                if ( p >= 0 ) {
                        ok ( p );
                        for ( int i = 0; i < n; ++i )
                                for ( int j = 0; j < m; ++j )
                                        printf ( "%d%c", f[ i ][ j ], j < m - 1 ? ' ' : '\n' );
                } else
                        puts ( "IMPOSSIBLE" );
        }

        return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值