トップページに戻る
次のC#のサンプルへ
前のC#のサンプルへ
Problem115 ブロックの組み合わせ方の数え上げ その2
問題
注意: これは Problem114をより難しくした問題である.
長さnユニットからなる 1 列上に, 最低mユニットの長さを持つ赤ブロックが置かれている.
ただしどの赤ブロック同士も, 少なくとも1ユニットの黒い正方形が間にある(赤ブロックは長さが異なってもよい).
敷き詰め計数関数 F(m, n) は 1列に敷き詰める方法が何通りかを表すとする.
例えば, F(3, 29) = 673135 であり, F(3, 30) = 1089155 である.
m=3 の時, n=30 がこの敷き詰め計数関数が初めて100万を超える最小の値であることが分かる.
同様に, m=10 では F(10, 56) = 880711, F(10, 57) = 1148904 であることがわかり,
つまり n=57 がこの敷き詰め計数関数が初めて100万を超える最小の値であることが分かる.
m=50 のとき, この敷き詰め計数関数が初めて100万を超える最小のnの値を求めよ.
ソース
using System;
class Program
{
static void Main()
{
Console.WriteLine("F({0},{1})={2}", 3, 29, ExecDP(3, 29));
Console.WriteLine("F({0},{1})={2}", 3, 30, ExecDP(3, 30));
Console.WriteLine("F({0},{1})={2}", 10, 56, ExecDP(10, 56));
Console.WriteLine("F({0},{1})={2}", 10, 57, ExecDP(10, 57));
for (int I = 1; I < int.MaxValue; I++) {
long wkCnt = ExecDP(50, I);
Console.WriteLine("F({0},{1})={2}", 50, I, wkCnt);
if (wkCnt > 1000000) break;
}
}
static long ExecDP(int pM, int pN)
{
//場合の数[残りのマス]なDP表
long[] DPArr = new long[pN + 1];
DPArr[pN] = 1;
for (int I = pN; 1 <= I; I--) {
if (DPArr[I] == 0) continue;
//赤を敷き詰める場合
for (int J = pM; J <= pN; J++) {
int NewI = I - J;
//マス目が不足する場合
if (NewI < 0) break;
//次の黒を敷き詰め
if (NewI > 0) NewI--;
DPArr[NewI] += DPArr[I];
}
//黒を敷き詰める場合
DPArr[I - 1] += DPArr[I];
}
return DPArr[0];
}
}
実行結果
省略
F(50,165)=848304
F(50,166)=910165
F(50,167)=978181
F(50,168)=1053389
解説
動的計画法を使ってます。