D. 505

传送门

分析

这个道题的题意是给你一个n * m的01矩阵,然后可以更改矩阵中的数字,比如把0改成1,或者把1改成0,要求最后矩阵中没有任何一个边长为偶数的子矩阵中含有偶数个1,如果不含偶数边长的子矩阵,则不需要改动

首先分析一下,我们能找到的最小的子矩阵应该是2 * 2 ,如果一个2 * 2矩阵内含有奇数个1,那么四个2 * 2矩阵拼接形成一个4 * 4矩阵中肯定就含有偶数个1,所以易得出,如果n和m同时大于3,那么一定无法构造出符合题意的矩阵

然后根据题意,n == 1的时候可以特判,所以我们需要讨论的情况只有n = 1的情况和n = 3的情况

首先来看n = 2的情况,如果我们需要让一个2 * 2的小矩阵内的1个数为0,不外乎一下几种情况

第一列:01 11 21 
第二列:11 01 11

所以只需要暴力枚举每一种情况即可

然乎再来分析n = 3的情况
首先每一列有3个数,对应2进制应该是0 - 8,而我们通过分析可以得出在每种状态下下一列可能的情况,分别对应两种,这个时候我们可以通过dp的思路来求解
假设我们把第i列的状态转化成j,那么第i + 1列的状态就只有两种,我们只需要计算出将第i列转化成j状态需要改动多少位数字,将第i + 1列转化为对应的状态需要改动多少位数字,然后两种转移方式取min即可,最后遍历最后一列0 - 8的状态所需要改动的数字的最小值,输出即可

代码

#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>

using namespace std;

const int N = 500006;
char a[4][N];
vector<int> st;
int n,m;
vector<int> g[8];
int f[N][8];

int lowbit(int x){ //统计二进制中1的个数
    return x & -x;
}

void res1(){
    int ans1 = 0,ans2 = 0;
    for(int i = 0;i < st.size();i += 2){
        if(st[i] == 0 ||st[i] == 3) ans1++;
        if((st[i + 1] == 1 || st[i + 1] == 2) && i + 1 < st.size()) ans1++;
    }
    for(int i = 0;i < st.size();i += 2){
        if(st[i] == 1 || st[i] == 2) ans2++;
        if(i + 1 < st.size() && (st[i + 1] == 0 || st[i + 1] == 3)) ans2++;
    }
    printf("%d",min(ans1,ans2));
}

void res2(){
    //各种状态下的转移矩阵
    g[0] = {5,2};g[1] = {4,3};
    g[2] = {7,0};g[3] = {1,6};
    g[4] = {1,6};g[5] = {0,7};
    g[6] = {4,3};g[7] = {5,2};
    for (int i = 0; i < 8; i++) {
        f[0][i] = lowbit(i ^ st[0]); // 统计转移成相应状态需要改动多少位
    }
    for(int i = 1;i < st.size();i++)
        for(int j = 0;j < 8;j++){ //枚举每一种状态
            f[i][j] = min(f[i - 1][g[j][0]] + lowbit(j ^ st[i]),  //相同为0,不同为1
                            f[i - 1][g[j][1]] + lowbit(j ^ st[i]));
            //当第i - 1列的状态为g[j][0]或者g[j][1]的时候为合法状态,进行状态转移
        }
    int ans = 0x3f3f3f3f;
    for(int i = 0;i < 8;i++) ans = min(ans,f[st.size() - 1][i]);
    printf("%d",ans);
}

int main(){
    scanf("%d%d",&n,&m);
    if(n >= 4 && m >= 4){
        puts("-1");
        return 0;
    }
    if(n == 1||m == 1){
        puts("0");
        return 0;
    }
    for(int i = 0;i < n;i++)
        scanf("%s",a[i]);
    for(int i = 0;i < m;i++){
        int x = 0;
        for(int j = 0;j < n;j++){
            if(j) x <<= 1;
            x += a[j][i] - '0';
        }
        st.push_back(x);
    }
    if(n == 2) res1();
    else res2();
    return 0;
}

猜你喜欢

转载自blog.csdn.net/tlyzxc/article/details/107947146