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

C++で解く平方数配列(Squareful配列)となる順列の個数


問題概要

正整数からなる配列 A が与えられたとします。すべての隣接する 2 要素の和が完全平方数であるとき、この配列を「平方数配列(Squareful 配列)」と呼びます。求めたいのは、A を並べ替えて作られる順列のうち、平方数配列となっているものの総数です。なお、あるインデックス i において A1[i] ≠ A2[i] となるとき、またそのときに限り、2 つの順列 A1 と A2 は「異なる」ものとみなします。

たとえば、入力が [3, 30, 6] のとき、答えは 2 になります。[3, 6, 30] と [30, 6, 3] という 2 通りの順列が条件を満たすためです(3 + 6 = 9、6 + 30 = 36 はいずれも完全平方数です)。

解き方のアプローチ

この問題は、バックトラッキング(引き戻し法)ですべての並べ替えを試しつつ、条件を満たさない枝を途中で切り捨てることで効率的に解けます。ポイントは次の 2 つです。

  • 完全平方数の判定: 関数 isSqr(n) は n の平方根 x を求め、x * x == n が成立すれば true を返します。
  • 重複順列の排除: 同じ位置に同じ値を置く操作は結果が重複するため、各再帰段階で set 型の visited を用意し、すでに試した値をスキップします。

手順の詳細

  1. 関数 isSqr(n): x := √n とし、(x * x) == n であれば true を返します。
  2. 関数 solve(a, idx):
    • idx が配列 a のサイズと等しい場合、count を 1 増やして return します。
    • 集合 visited を用意します。
    • i := idx から配列の末尾までループします。
      • (idx == 0 または isSqr(a[idx - 1] + a[i])) かつ a[i] が visited に存在しない場合:
        • a[idx] と a[i] を交換します。
        • solve(a, idx + 1) を再帰呼び出しします。
        • a[idx] と a[i] を元に戻します(バックトラック)。
        • a[i] を visited に追加します。
  3. メイン処理: count := 0 で初期化し、solve(a, 0) を実行した後、count を返します。

最悪の場合の探索量は順列の総数に依存しますが、隣接要素の和による早期の枝刈りと重複値のスキップにより、実際に探索すべき状態は大幅に絞り込まれます。

C++ 実装例

#include <bits/stdc++.h>
using namespace std;
typedef long long int lli;
class Solution {
   public:
   int count;
   bool isSqr(lli n){
      lli x = sqrt(n);
      return x * x == n;
   }
   void solve(vector<int>& a, int idx){
      if (idx == a.size()) {
         count++;
         return;
      }
      set<int> visited;
      for (int i = idx; i < a.size(); i++) {
         if ((idx == 0 || isSqr(a[idx - 1] + a[i])) &&
         !visited.count(a[i])) {
            swap(a[idx], a[i]);
            solve(a, idx + 1);
            swap(a[idx], a[i]);
            visited.insert(a[i]);
         }
      }
   }
   int numSquarefulPerms(vector<int>& a){
      count = 0;
      solve(a, 0);
      return count;
   }
};
main(){
   Solution ob;
   vector<int> v = {3,30,6};
   cout << (ob.numSquarefulPerms(v));
}

入力

{3,30,6}

出力

2

  1. C++で質素数(Frugal Number)を判定する方法【サンプルコード付き】

    この記事では、正の整数 N が与えられたときに、その数が質素数(Frugal Number)であるかどうかを判定するプログラムを C++ で作成する方法を解説します。 質素数とは? 質素数(FRUGAL NUMBER)とは、その数自身の桁数が、素因数分解による表現の桁数よりも厳密に大きい数のことです。 例:625 の場合 625 を素因数分解すると 54 となります。 625 自身の桁数:3 桁 54 の表現の桁数:2 桁 3 は 2 よりも厳密に大きいため、625 は質素数です。 最初のいくつかの質素数:125、128、243、256、343、512、625 など 問題を理解するための具

  2. C++で五胞体数(ペンタトープ数)を求める方法

    五胞体数とは? 五胞体数(ペンタトープ数)は、パスカルの三角形の第5の対角線上に現れる数列として知られています。この数列を定義するには、パスカルの三角形に少なくとも5つの数が必要となるため、数列の最初の数はパスカルの三角形の第4行である 1 4 6 4 1 から始まります。 本チュートリアルでは、n番目の五胞体数を求める方法を解説します。まずは具体的な例を見てみましょう。 入力 : 1出力 : 1入力 : 4出力 : 35 以下の図から出力を確認できます。 この問題は数列に関するものなので、解法ではまず数列のパターンを見つけることから始めます。 解法のアプローチ このプログラムでは、数列の