C++
 Computer >> コンピューター >  >> プログラミング >> C++

C++でマトリックス(行列)を反転した後に得られる最大スコアの求め方

0と1のみで構成される二次元マトリックス(行列)Aがあるとします。ここでいう「操作」とは、任意の行または列を選び、その行・列内のすべての値を反転させること、つまり0を1に、1を0に一括変更することを指します。任意の回数だけ操作を行った後、マトリックスの各行は2進数として解釈され、マトリックス全体のスコアはこれらの数値の総和となります。今回の課題は、このスコアとして考えられる最大値を求めることです。

例えば、入力が以下のようなマトリックスだった場合を見てみましょう。

0011
1010
1100

最適な操作を行った結果、出力は 39 になります。これは反転後のマトリックスが次のようになるためです。

1111
1001
1111

このとき各行が表す数値は 1111₂ = 15、1001₂ = 9、1111₂ = 15 となり、合計は 15 + 9 + 15 = 39 です。

解法のポイント:貪欲法(グリーディー法)

この問題を効率よく解くには、貪欲法を使います。重要な性質は次のとおりです。

  • 最上位ビットは必ず1にできる: 各行を独立に反転できるため、すべての行の先頭(最上位桁)は必ず1にできます。最上位ビットは他のどの桁よりも重みが大きいため、これを優先的に1にするのが最善です。

  • それ以外の列は多数決で決める: 先頭列が1になった状態では、ある列jについて「A[i][j] と A[i][0] が等しい行の数」を数えれば、反転によって作れる1の個数がわかります。cnt と n − cnt のうち大きい方の数だけ1にできます。

アルゴリズムの手順

  • n := 行数、m := 列数とする

  • ret := n × 2(m − 1) を初期値とする(全行の最上位ビットが1になると仮定)

  • j = 1 から m − 1 まで繰り返す

    • cnt := A[i][j] == A[i][0] を満たす行 i の個数

    • temp := 2(m − j − 1) × max(cnt, n − cnt)

    • ret := ret + temp

  • ret を返す

それでは、実際の実装例を見て理解を深めましょう。

C++実装例

#include <bits/stdc++.h>
using namespace std;
class Solution {
    public:
    int matrixScore(vector<vector<int>>& A) {
        int n = A.size();
        int m = A[0].size();
        int ret = (1 << (m - 1)) * n;
        for(int j = 1; j < m; j++){
            int cnt = 0;
            for(int i = 0; i < n; i++){
                cnt += (A[i][j] == A[i][0]);
            }
            int temp = ((1 << (m - (j + 1))) * max(cnt, n - cnt));
            ret += temp;
        }
        return ret;
    }
};
main(){
    vector<vector<int>> v = {{0,0,1,1},{1,0,1,0},{1,1,0,0}};
    Solution ob;
    cout << (ob.matrixScore(v));
}

入力

[[0,0,1,1],[1,0,1,0],[1,1,0,0]]

出力

39

この実装では、行・列を実際に反転させる処理を行わずに、ビット演算とカウントだけで最大スコアを計算しています。そのため計算量は O(n × m) と非常に効率的で、大きなマトリックスでも高速に動作します。実際に反転操作をシミュレーションする必要がない点が、この貪欲法アプローチの大きな利点といえるでしょう。

  1. C++で行列を走査する方法:行優先トラバーサルと列優先トラバーサルの徹底解説

    行列の走査には2つの方法がある2次元行列(マトリックス)の要素を訪問する方法は、大きく分けて2種類あります。行優先(Row-wise)トラバーサルでは、1行目から順に、各行の要素を先頭のインデックスから最後のインデックスまで左から右へと訪問していきます。すべての行を処理し終えるまで、これを繰り返します。一方、列優先(Column-wise)トラバーサルでは、1列目から最終列目へ向かって、各列の要素を上から下へと順番に訪問します。インデックスの基本的な考え方2次元行列 M[i][j] において、インデックス i は行、インデックス j は列を表します。行優先トラバーサルの場合は、次の順序でアクセ

  2. C++で解説:T秒後のカエルの位置を求める確率計算アルゴリズム

    n個の頂点からなる無向木(ツリー)があるとします。頂点には1からnまでの番号が付けられており、カエルは頂点1からジャンプを開始します。カエルは、現在いる頂点に隣接している「未訪問」の頂点へ、1秒でジャンプすることができますが、一度訪れた頂点へ戻ることはできません。ジャンプ先の候補が複数ある場合は、いずれも等しい確率でランダムに1つを選んで移動します。逆に、行ける未訪問の頂点がなくなったカエルは、その場で永遠に跳ね続けることになります。 木は辺の配列として与えられます。ここで求めたいのは、「t秒後にカエルが頂点targetの上にいる確率」です。 問題の例 たとえば、入力が n = 7、t = 2