LeetCode第 861 题:翻转矩阵后的得分(C++)

861. 翻转矩阵后的得分 - 力扣(LeetCode)

在这里插入图片描述

可以进行的操作是行变换列变换,最终的目的是要使得最后的数字和最大。

行变换只会影响一个数字(该行的数字)。由于矩阵的 0/1 呈现的是二进制格式(数字是按照行计算的),所以位1越靠右数字就会越大。不难得出,第一位不是1的数字(示例中最大为0111 = 7)肯定小于第一位是1的数字(示例中最小为1000 = 8 > 7),所以如果某一行的第一位不是1,那为达目的就需要进行行变换。相反地,如果第一位已经是1了,那就不能进行行变换了。

进行完上面的行变换之后,矩阵的第一列全部是1:

0 0 1 1				1 1 0 0
1 0 1 0   =>       1 0 1 0
1 1 0 0               1 1 0 0

行变换的场景清楚了,那列变换需要怎么使用呢?列变换会同时影响到每一行的数字,似乎有点不好搞。但是,我们最终的目的是什么?是要使所有数字的和最大,举个例子
,看上面第一列变为1之后的矩阵按二进制展开累加(1,2,4,8,16…那样展开):

1 1 0 0	=  8 + 4 + 0 + 0  =  12
1 0 1 0   =  8 + 0 + 2 + 0  =  10  ==>  12 + 10 + 12 = 34
1 1 0 0   =  8 + 4 + 0 + 0  =  12

矩阵的和34来自于三个数12,10,12的相加,而这三个数又来自于矩阵的0/1分布,准确说是来自于1的分布,而1的分布位置对于三个数的贡献是一样的:比如从左往右数,第二个位置,这个位置的1产生的贡献是4,该例子中这个4体现在第一个、第三个数,最终又体现在和34中。想要使得最终的和最大,是不是这一列(第二列)的贡献4越多越好呢,或者说是不是第二列1的个数越多越好呢?答案是肯定的!原因还是由于二进制数的性质:100000…000(x个0) > 011111…111(x个1)。所以,我们是否需要进行列变换的依据就是1的个数是否多于0的个数,如果1的个数更少,就要进行列变换保证1的个数比0的个数多。

把上面的逻辑代码表示出来,计算最后的和:

class Solution {
public:
    int cnt[22];//计数器,记录每一列1的个数
    int matrixScore(vector<vector<int>>& A) {
        int m = A.size(), n = A[0].size(), res = 0;
        for(int i = 0; i < m; ++i){//行变换
            if(A[i][0] == 0){
                for(int j = 0; j < n; ++j)  A[i][j] ^= 1;
            }
        }

        for(int j = 1; j < n; ++j){
            for(int i = 0; i < m; ++i) //记录第j列1的个数
                if(A[i][j] == 1)    ++cnt[j];
            if(cnt[j] < m - cnt[j]){ //第j列1的个数小于0的个数
                for(int i = 0; i < m; ++i)  A[i][j] ^= 1;//列变换
            }
        }

        //计算最后的和
        int v = pow(2, n-1);//最左边二进制1对应的值
        for(int j = 0; j < n; ++j){//一列一列地累加
            for(int i = 0; i < m; ++i)
                if(A[i][j] == 1)    res += v;
            v >>= 1;
        }
        return res;
    }
};

上面只是体现了逻辑,但是事实上最后和的计算完全不用这么麻烦,只需要记住每一列1的个数,就可以直接求出该列产生的贡献:

class Solution {
public:
    int cnt[20];//计数器,记录每一列1的个数
    int matrixScore(vector<vector<int>>& A) {
        int m = A.size(), n = A[0].size(), res = 0;
        for(int i = 0; i < m; ++i){//行变换
            if(A[i][0] == 0){
                for(int j = 0; j < n; ++j)  A[i][j] ^= 1;
            }
        }
        cnt[0] = m; //第一列1的个数为m(行数)
        for(int j = 1; j < n; ++j){
            for(int i = 0; i < m; ++i) //记录第j列1的个数
                if(A[i][j] == 1)    ++cnt[j];
            if(cnt[j] < m - cnt[j]){ //第j列1的个数小于0的个数
                cnt[j] = m - cnt[j];//更新这一列1的个数
                for(int i = 0; i < m; ++i)  A[i][j] ^= 1;//列变换
            }
        }

        //计算最后的和
        int v = pow(2, n-1);//最左边二进制1对应的值
        for(int j = 0; j < n; ++j){//一列一列地累加
            res += v*cnt[j];
            v >>= 1;
        }
        return res;
    }
};

然后,计算和的步骤可以与列变换操作进行合并,因为其实没必要真正地进行列变换,我们只需要记录这一列的二进制位1的个数就可以求得最终的和:

class Solution {
public:
    int cnt[20];//计数器,记录每一列1的个数
    int matrixScore(vector<vector<int>>& A) {
        int m = A.size(), n = A[0].size(), res = 0;
        for(int i = 0; i < m; ++i){//行变换
            if(A[i][0] == 0){
                for(int j = 0; j < n; ++j)  A[i][j] ^= 1;
            }
        }
        int v = pow(2, n-1);//最左边二进制1对应的值
        for(int j = 0; j < n; ++j){
            for(int i = 0; i < m; ++i) //记录第j列1的个数
                if(A[i][j] == 1)    ++cnt[j];
            if(cnt[j] < m - cnt[j])//第j列1的个数小于0的个数
                res += v * (m - cnt[j]);
            else
                res += v * cnt[j];
            v >>= 1;//v除以2
        }
        return res;
    }
};

到了这儿,其实数组cnt也是没有必要的,一个int值已足够:

class Solution {
public:
    int matrixScore(vector<vector<int>>& A) {
        int m = A.size(), n = A[0].size();
        for(int i = 0; i < m; ++i){//行变换
            if(A[i][0] == 0)
                for_each(A[i].begin(), A[i].end(), [](int &v){ v ^= 1; });
        }
        int v = 1 << (n-1), res = 0;//最左边二进制1对应的值、结果
        for(int j = 0, cnt = 0; j < n; ++j, cnt = 0){
            for(int i = 0; i < m; ++i) //记录第j列1的个数
                if(A[i][j] == 1)    ++cnt;
            res += cnt < m - cnt ? v * (m - cnt) : v * cnt;
            v >>= 1;//v除以2
        }
        return res;
    }
};

总之就是越优化到后面代码越抽象,甚至行变换这一步都是不必要的,因为元素非1即0,每次在列上面计数的时候,只需要考虑该元素对应的行首是否是1,就知道是否应该增加计数器了,这也是官方题解的思路:翻转矩阵后的得分 - 翻转矩阵后的得分 - 力扣(LeetCode)

class Solution {
public:
    int matrixScore(vector<vector<int>>& A) {
        int m = A.size(), n = A[0].size(), v = 1 << (n-1), res = m*v;
        for(int j = 1, cnt = 0; j < n; ++j, cnt = 0){
            for(int i = 0; i < m; ++i)  cnt += A[i][0] == 1 ? A[i][j] : 1 - A[i][j]; 
            res += max(cnt, m - cnt) * (v >>= 1);
        }
        return res;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值