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

C++でAとBのXORがCと等しくなるようにフリップする最小ビットをカウントします

長さNの3つの2進数列 A、B、C が与えられます。それぞれの数列は1つの2進数を表しています。このとき、A と B のXOR(排他的論理和)の結果が C と一致するようにするために、A または B のビットを反転(フリップ)する必要のある最小回数を求めるのが本記事の目的です。

XOR演算の真理値表

まず、XOR演算の真理値表をおさらいしておきましょう。

XYX XOR Y
000
011
101
110

上の表からわかるように、X と Y が同じ値であれば X XOR Y は 0 となり、異なる値であれば 1 となります。この性質を利用することで、A と B のどのビットを反転すれば C に到達できるかを効率的に判定できます。

反転が必要かどうかの判定条件

各桁 i について、以下の4つのケースに分けて考えます。

  • A[i] == B[i] かつ C[i] == 0 の場合 → 反転は不要
  • A[i] == B[i] かつ C[i] == 1 の場合 → A[i] または B[i] のどちらか一方を反転し、カウントを +1
  • A[i] != B[i] かつ C[i] == 0 の場合 → A[i] または B[i] のどちらか一方を反転し、カウントを +1
  • A[i] != B[i] かつ C[i] == 1 の場合 → 反転は不要

入力例と出力例

例1

入力:

A[] = { 0,0,0,0 } B[] = { 1,0,1,0 } C[] = { 1,1,1,1 }

出力:

Required flips : 2

解説:

A[0] xor B[0] → 0 xor 1 = 1、C[0]=1 なので反転不要
A[1] xor B[1] → 0 xor 0 = 0、C[1]=1 なので反転(カウント=1)
A[2] xor B[2] → 0 xor 1 = 1、C[2]=1 なので反転不要
A[3] xor B[3] → 0 xor 0 = 0、C[3]=1 なので反転(カウント=2)

例2

入力:

A[] = { 0,0,1,1 } B[] = { 0,0,1,1 } C[] = { 0,0,1,1 }

出力:

Required flips : 2

解説:

A[0] xor B[0] → 0 xor 0 = 0、C[0]=0 なので反転不要
A[1] xor B[1] → 0 xor 0 = 0、C[1]=0 なので反転不要
A[2] xor B[2] → 1 xor 1 = 0、C[2]=1 なので反転(カウント=1)
A[3] xor B[3] → 1 xor 1 = 0、C[3]=1 なので反転(カウント=2)

アルゴリズムのアプローチ

  • 配列 a[]、b[]、c[] にそれぞれ2進数の各ビットを格納します。
  • 関数 flipCount(int A[], int B[], int C[], int n) は、配列 a、b、c とその長さ n を引数として受け取り、「A XOR B」が C と一致するようにするために A[] または B[] のビットを反転する必要のある回数を返します。
  • 変数 count は反転回数を表し、初期値は 0 です。
  • forループを使って、i = 0 から i = N-1 まで各ビットを順番に走査します。
  • 各ビットについて、A[i] と B[i] が等しく C[i] が 1 であれば count をインクリメントします。
  • 同様に、A[i] と B[i] が異なり C[i] が 0 であれば count をインクリメントします。
  • 最後に count を結果として返します。

このアルゴリズムの計算量は O(N) であり、配列を一度走査するだけで答えが求まるため非常に効率的です。

C++での実装例

#include<bits/stdc++.h>
using namespace std;
int flipCount(int A[], int B[], int C[], int N){
    int count = 0;
    for (int i=0; i < N; ++i){
        // A[i] と B[i] が等しい場合、XORの結果は0。C[i] が 1 なら反転が必要
        if (A[i] == B[i] && C[i] == 1)
            ++count;
        // A[i] と B[i] が異なる場合、XORの結果は1。C[i] が 0 なら反転が必要
        else if (A[i] != B[i] && C[i] == 0)
            ++count;
    }
    return count;
}
int main(){
    // N はビットの総数を表す
    int N = 5;
    int a[] ={1,0,0,0,0};
    int b[] ={0,0,0,1,0};
    int c[] ={1,0,1,1,1};
    cout <<"Minimum bits to flip such that XOR of A and B equal to C :"<<flipCount(a, b, c,N);
    return 0;
}

出力

Minimum bits to flip such that XOR of A and B equal to C :2

まとめ

本記事では、2進数列 A と B のXORが C と一致するようにするために必要な最小ビット反転回数を求める方法を解説しました。ポイントは、XORの真理値表の性質を利用して「同じ値なら0、異なる値なら1」という規則性に基づき、各桁ごとに反転の要否を線形時間 O(N) で判定できることです。シンプルな条件分岐だけで実装できるため、競技プログラミングやビット演算の基礎学習にも最適な問題といえます。

  1. C++で「x + 桁の合計 = n」を満たす数xを見つける方法

    この記事では、ある整数 n が与えられたとき、「x + x の各桁の合計 = n」という条件を満たす数 x を求める問題を解説します。例として、n = 21 の場合を考えてみましょう。このとき答えは x = 15 となります。なぜなら、15 の各桁の合計は 1 + 5 = 6 であり、15 + 6 = 21 となって、与えられた n と一致するからです。解き方のアプローチこの問題はシンプルな方法で解くことができます。1 から n まで順番に数を調べていき、それぞれの数について「その数自身 + 各桁の合計」が n と等しくなるかどうかを確認します。条件を満たす数が見つかった時点で処理を終了し、そ

  2. 【C++】2つのバイナリ配列のXORを別の配列と等しくするための最小フリップ回数

    問題文 0と1のみから構成される、長さnの3つの配列が与えられます。求めたいのは、1つ目と2つ目の配列のビットをできるだけ少ない回数反転(フリップ)させて、「1つ目の配列のi番目の要素」と「2つ目の配列のi番目の要素」のXORが、「3つ目の配列のi番目の要素」と一致するようにするための最小反転回数です。 ただし、配列1については最大p個、配列2については最大q個までしかビットを反転できません。また、配列の要素を並べ替えることは許されていません。 ここでは、p = 2、q = 5 の場合を例に考えてみましょう。 arr1[] = {1, 0, 1, 1, 0, 1, 0} arr2[] = {