C++で解くドミノとトロミノを使ったタイル敷き詰め問題(2×Nボード)
問題の概要
本記事では、「ドミノ」と「トロミノ」という2種類の形状を使ったタイル敷き詰め(タイリング)問題をC++で解く方法を解説します。これらのピースは、以下のように回転させて使用することができます。
タイリングでは、盤面上のすべてのマスを必ずタイルで覆わなければなりません。また、2つのタイリング方法は、盤上の4方向に隣接する2つのセルにおいて、片方のタイリングだけがその両方のマスを同じタイルで占有している場合に限り「異なる」とみなされます。
入力と出力の例
整数Nが与えられたとき、2×Nのボードを敷き詰める方法が何通りあるかを求めます。例えば、入力が3の場合、出力は5となります。敷き詰め方は以下の通りです(異なる文字は異なるタイルを表します)。
- [XYZ XXZ XYY XXY XYY]
- [XYZ YYZ XZZ XYY XXY]
解法のアプローチ
この問題は動的計画法(DP)を用いることで効率的に解くことができます。手順は以下の通りです。
- サイズN+5の配列dpを用意し、初期値として dp[1] := 1、dp[2] := 2、dp[3] := 5 を設定する
- iが4からNまでの範囲で、以下の漸化式を繰り返し適用する
dp[i] := 2 × dp[i−1] + dp[i−3] - 最後に dp[N] を答えとして返す
この漸化式の考え方は、新しい列を追加する際に「縦長ドミノ1枚」「横並びドミノ2枚」などのパターンと、突起を持つ不完全な状態からの遷移を組み合わせることで、全ての敷き詰め方を数え上げることができるというものです。
C++による実装例
それでは、実際のコードを見ていきましょう。答えが非常に大きくなる可能性があるため、109+7で割った余りを計算しています。
#include <bits/stdc++.h>
using namespace std;
const int MOD = 1e9 + 7;
int add(int a, int b){
return ((a % MOD) + (b % MOD)) % MOD;
}
class Solution {
public:
int numTilings(int N) {
vector <int> dp(N + 5);
dp[1] = 1;
dp[2] = 2;
dp[3] = 5;
for(int i = 4; i <= N; i++){
dp[i] = add(2 * dp[i - 1], dp[i - 3]);
}
return dp[N];
}
};
main(){
Solution ob;
cout << (ob.numTilings(3));
}実行結果
入力
3
出力
5
まとめ
このように、ドミノとトロミノによる2×Nボードの敷き詰め問題は、漸化式 dp[i] = 2×dp[i−1] + dp[i−3] を用いた動的計画法によってO(N)の計算量で解くことができます。初期条件 dp[1]=1、dp[2]=2、dp[3]=5 を正しく設定することが重要なポイントです。
-
C++で解く「壁と門」問題:BFSによる最短距離計算の徹底解説
問題概要m × n の2次元グリッドを考えます。このグリッドは、以下の3種類の値で初期化されています。-1:壁または障害物0:ゲート(門)INF:空き部屋(無限大を表す)ここでは、INF として 2^31 − 1 = 2147483647 を使用します。ゲートまでの距離は必ず 2147483647 未満になると仮定できるためです。求めたいのは、各空き部屋に対して、最も近いゲートまでの距離です。もしゲートへ到達できない部屋があれば、その部屋は INF のままにします。入力例INF-10INFINFINFINF-1INF-1INF-10-1INFINF出力例3-101221-11-12-10-13
-
C++で円と長方形の重なりを判定するアルゴリズム
問題の概要円を (radius, xc, yc) という形式で表します。ここで (xc, yc) は円の中心座標です。同様に、軸に平行な長方形(軸平行境界ボックス)を (x1, y1, x2, y2) という形式で表し、(x1, y1) が左下隅の座標、(x2, y2) が右上隅の座標とします。このとき、円と長方形が互いに重なっているかどうかを判定する必要があります。たとえば、次のような入力が与えられた場合を考えてみましょう。この場合、出力は true(重なりあり)となります。解決のアプローチこの問題を解く鍵は、「長方形の中で円の中心に最も近い点」を見つけることです。その点と円の中心との距離が