4218. 翻转

题目:

给定一个 M×N 的 01 矩阵。

你需要选择其中一些元素,并对选择的元素进行翻转操作。

翻转操作是指将所选元素以及与其上下左右相邻的元素(如果有)进行翻转(0 变 1,1 变 0)。

我们希望最终矩阵变为一个全 0 矩阵,并且选择进行翻转操作的元素数量尽可能少。

输出最佳翻转方案。

输入格式:

第一行包含整数 M,N。

接下来 M 行,每行包含 N 个整数,每个整数为 0 或 1。

输出格式:

共 M 行,每行包含 N 个整数,其中第 i 行第 j 列的整数,表示第 i 行第 j 列元素的翻转次数。

如果翻转操作次数最少的操作方案不唯一,则输出字典序最小的方案。

如果不存在合理方案,则输出 IMPOSSIBLE。

样例输入:

4 4
1 0 0 1
0 1 1 0
0 1 1 0
1 0 0 1

样例输出:

0 0 0 0
1 0 0 1
1 0 0 1
0 0 0 0

解题思路:

当第一行翻转完后, 想要去掉第一行的1, 需要翻转第二行对应列的位置。

依次类推, 当第i行确定时, 只有i+1行对应列的位置翻转才会影响到它。

对于相同的位置, 要么翻一次, 要么不翻, 因为翻两次会恢复原样, 翻第三次相当于只翻了一次。

数据范围在[1, 15], 第一行的情况只有 2^15 种, 暴力枚举第一行(翻转情况,0代表不翻转,1代表翻转)即可。

对于每种第一行的情况, 全部翻转完后, 遍历一次最后一行, 如果有1说明该方案不可行, 否则更新最优解。

题解代码:

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 20;

const int dx[]= {-1, 0, 1, 0};
const int dy[]= {0, 1, 0, -1};

int g[N][N],back[N][N],m,n;//g为临时地图,back保存初始状态
int res[N][N],tmp[N][N];//最优答案,临时答案

void fanzhuan(int x,int y) {
    g[x][y]^=1;//0变1,1变0

    for(int i=0; i<4; i++) {
        int X=dx[i]+x;
        int Y=dy[i]+y;
        if(X<0||X>=n||Y<0||Y>=m)continue;
        g[X][Y]^=1;
    }

}

signed main() {

    cin>>n>>m;

    int ans=0x3f3f3f3f;

    for(int i=0; i<n; i++) {
        for(int j=0; j<m; j++) {
            cin>>g[i][j];//原地图
            back[i][j]=g[i][j];
            //备份地图,每次只需要改变第一行的状态即可
        }
    }

    for(int op=0; op<(1<<m); op++) {
        //第一行的所有情况
        memcpy(g,back,sizeof g);//恢复现场
        memset(tmp,0,sizeof tmp);
        int cnt=0;

        for(int j=0; j<m; j++) {
            if(op>>j&1) {//是1代表这里翻转,得到一个状态
                fanzhuan(0,m-j-1);
                cnt++;
                tmp[0][m-j-1] = 1;
            }
        }

        for(int j = 1; j < n; j++) { //翻转第二行到最后一行
            for(int k = 0; k < m; k++)
                if(g[j - 1][k]) fanzhuan(j, k), cnt++, tmp[j][k] = 1;
        }

        bool f=false;
        for(int j = 0; j < m; j++)  //遍历最后一行
            if(g[n - 1][j]) {
                f = true;
                break;
            }

        if(f)continue;

        if(ans>cnt) {
            ans=cnt;
            memcpy(res, tmp, sizeof res);
        }
    }

    if(ans == 0x3f3f3f3f) cout << "IMPOSSIBLE";
    else {
        for(int i = 0; i < n; i++) {
            for(int j = 0; j < m; j++)
                cout << res[i][j] << ' ';
            cout << '\n';
        }
    }

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值