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

C++で解く「2倍ペアに並べ替えられるか」判定アルゴリズム

問題概要

長さが偶数の整数配列 A が与えられます。この配列を並べ替えることで、すべての 0 <= i < len(A) / 2 について A[2 * i + 1] = 2 * A[2 * i] が成り立つようにできる場合のみ true を返します。

例えば、入力が [3, 1, 3, 6] の場合は false を返し、[4, -2, 2, -4] の場合は true を返します。後者は [-2, -4][2, 4] というペアに分けられるためです。

解き方の考え方

この問題は、各要素の出現回数を記録したマップ(連想配列)を使って、小さい値から順に「自分の2倍の値」とマッチングさせていくことで解けます。手順は以下の通りです。

  • マップ m を作成し、配列 A の各要素の出現回数を格納する
  • cnt := 配列 A のサイズとする(まだ処理されていない要素数)
  • マップ内の各キーと値のペア kv について以下を繰り返す
    • m[kv.key] > 0 の場合:
      • kv.key が 0 ではなく、かつ m[2 * kv.key] > 0 であれば、x := min(m[kv.key], m[2 * kv.key]) として、cnt から x * 2 を引き、両方のカウントから x を減らす
      • kv.key が 0 の場合は、0 同士がペアになるため cnt から m[0] を引いて m[0] を 0 にする
  • 最終的に cnt が 0 でなければ false、0 なら true を返す

std::map はキーが昇順にソートされるため、負の値から順に処理される点にも注意してください。負の数の場合、2倍するとより小さい値になるため、昇順処理によって正しくペアリングできます。

C++実装例

#include <bits/stdc++.h>
using namespace std;
class Solution {
   public:
   bool canReorderDoubled(vector<int>& A) {
      map <int, int> m;
      int n = A.size();
      for(int i = 0; i < n; i++){
         m[A[i]]++;
      }
      int cnt = A.size();
      map <int, int> :: iterator it = m.begin();
      while(it != m.end()){
         if(m[it->first] > 0){
            if(it->first != 0 && m[it->first * 2] > 0){
               int x = min(m[it->first], m[it->first * 2]);
               cnt -= (x * 2);
               m[it->first * 2] -= x;
               m[it->first] -= x;
            }else if(it->first == 0){
               cnt -= m[it->first];
               m[it->first] = 0;
            }
         }
         it++;
      }
      return !cnt;
   }
};
main(){
   vector<int> v1 = {3,1,3,6};
   Solution ob;
   cout << (ob.canReorderDoubled(v1)) << endl;
   v1 = {4,-2,2,-4};
   cout << (ob.canReorderDoubled(v1));
}

入力

[3,1,3,6]
[4,-2,2,-4]

出力

0
1

最初の入力 [3,1,3,6] では、6 の半分である 3 は存在しますが、3 の半分や 6 の2倍に対応する要素が足りず、すべてをペアにできないため 0(false)が出力されます。一方、[4,-2,2,-4] では -2 → -42 → 4 のペアが成立するため 1(true)が出力されます。

計算量

時間計算量は O(n log n)(マップへの挿入と走査)、空間計算量は O(n) となります。

  1. C++で文字列の配列を定義・操作する方法を解説

    この記事では、C++において文字列の配列をどのように定義し、扱うのかを詳しく解説します。C言語との違い:文字列配列の基礎知識C言語には文字列型が存在しないため、文字列はchar型の配列(文字配列)として表現する必要がありました。そのため、複数の文字列をまとめて管理する「文字列の配列」を作るには、2次元のchar型配列を用意し、各行に異なる文字列を格納するという手法が取られていました。これは直感的ではなく、コードも冗長になりがちでした。一方、C++ではstd::stringクラスが標準ライブラリとして提供されています。このクラスのオブジェクトを使えば、文字列データを効率的かつ安全に格納・操作でき

  2. C++で配列を並べ替える方法|選択ソートの仕組みと実装例を解説

    C++では、さまざまなソート(並べ替え)アルゴリズムを使って配列を整列できます。ソート済みの配列とは、数値の大小順やアルファベット順など、何らかの基準に従って要素が並び替えられた配列のことです。代表的なソートアルゴリズムには、バブルソート、挿入ソート、選択ソート、マージソート、クイックソート、ヒープソートなどがあります。本記事では、その中でも構造がシンプルで理解しやすい「選択ソート」を取り上げ、実際のコード例とともに詳しく解説していきます。 選択ソートとは? 選択ソートは、未ソート部分の中から最小値を繰り返し探し出し、それを未ソート部分の先頭にある要素と交換することで、配列全体を昇順に整列さ