【C++解説】nを表すために必要な最小の完全平方数の個数を動的計画法で求める
正の整数 n が与えられたとき、その和がちょうど n に等しくなるような完全平方数の最小個数を求める問題を考えます。
例えば n = 10 の場合を考えてみましょう。10 = 9 + 1 と表せるため、必要な完全平方数は 2 個となり、出力は 2 になります。
解法のアプローチ(動的計画法)
この問題は、動的計画法(DP)を使うことで効率的に解くことができます。手順は以下の通りです。
- 長さ n + 1 の DP テーブルを作成し、すべての要素を無限大(INF)で初期化する
- dp[0] = 0 と設定する(0 を作るのに必要な個数は 0)
- i = 1 から始めて、i × i ≤ n を満たす間、以下を繰り返す
- x = i × i(現在の完全平方数)とする
- j を x から n まで動かしながら、dp[j] を「dp[j]」と「1 + dp[j − x]」の小さい方で更新する
- 最後に dp[n] を返す
この DP の考え方は、「j という数を作るには、ある完全平方数 x を 1 つ使った残りの j − x を作る個数に 1 を加えたもの」という遷移をすべての平方数について試す、というものです。
C++での実装例
それでは、実際のコードを見てみましょう。
#include<bits/stdc++.h>
using namespace std;
#define INF 1e9
class Solution {
public:
int solve(int n) {
vector < int > dp(n+1,INF);
dp[0] = 0;
for(int i =1;i*i<=n;i++){
int x = i*i;
for(int j = x;j<=n;j++){
dp[j] = min(dp[j],1+dp[j-x]);
}
}
return dp[n];
}
};
main(){
Solution ob;
cout << ob.solve(10);
}入力
10
出力
2
コードのポイント
- 初期化: dp 配列を INF で埋めることで、「まだ到達できない状態」を明確に区別できます。
- 計算量: 外側のループは √n 回、内側のループは最大 n 回回るため、全体の時間計算量は O(n√n) となります。
- 実用性: この手法は有名な「四平方定理(ラグランジュの定理)」に関連しており、あらゆる自然数は高々 4 つの完全平方数の和で表せることが知られています。
このように、動的計画法を活用することで、一見複雑な組み合わせ最適化の問題もシンプルかつ効率的に解くことができます。
-
サイズ d の正十二角形を作れる組み合わせの数を求める C++ プログラム
問題概要 整数 d が与えられたとします。ここで、一辺の長さが 1 の正方形タイルと正三角形タイルが無限枚あるものと考えます。これらのタイルを組み合わせて、一辺の長さが d の正十二角形(12 辺形)を作るとき、その作り方が何通りあるかを求めるのがこの問題です。答えが非常に大きくなる場合は、998244353 で割った余りを返します。 アプローチ この問題は、二項係数を利用することで効率的に解くことができます。結論から言うと、求めるべき答えは C(2d−1, d−1)、すなわち「2d−1 個の中から d−1 個を選ぶ組み合わせの総数」です。 階乗を直接計算すると値が急激に大きくなりオーバー
-
【C++】長方形に含まれる正方形の総数を求めるアルゴリズムと実装
縦の長さL、横の幅B(L≥B)の長方形が与えられたとします。この記事では、L×Bの長方形の中にいくつの正方形が含まれているかを効率的に求める方法を解説します。 上の図は3×2の長方形の例です。この長方形には、2×2の正方形が2個、1×1の正方形が6個含まれています。 合計:6+2=8個 規則性を見つける まず、正方形だけで構成されたB×Bの図形について考えてみましょう。 サイズL×Bの長方形には、必ずL×B個の1×1の正方形が含まれます。 含まれる最大の正方形のサイズはB×Bです。 L=B=1の場合:正方形の数=1 L=B=2の場合:正方形の数=1+4=5(2×2が1個、1×1が4個) L