トップページに戻る
次のC#のサンプルへ
前のC#のサンプルへ
遊び倒すガイド 上級編 バッタのジャンプの場合の数
問題
6枚のタイルが横1列に並んでいる。
いま、左端のタイル(S)上に1匹のバッタがいる。
バッタは次のルールにしたがってジャンプを繰り返し、右端のタイル(G)に向かう。
●バッタは右または左方向にジャンプする。任意の枚数のタイルを飛び越え、他のタイルに着地する。
●バッタはジャンプすると、次はその反対方向にジャンプする。
●バッタはGに到着する前に、他のすべてのタイルにちょうど1度ずつ着地する。Sは始めから着地済みとみなす。
このようにGへ到着する仕方は、下記の5通りある。
20枚のタイルが横1列に並ぶとき、Gへ到着する仕方は何通りあるか?
プロジェクトオイラーを遊び倒すガイド (上級編)
ソース(動的計画法)
using System;
using System.Linq;
class Program
{
//const long TileCnt = 6 - 2;
const long TileCnt = 20 - 2;
static void Main()
{
//場合の数[左のタイル数,右のタイル数]なDP表
long[,] PrevDP = new long[TileCnt + 1, TileCnt + 1];
PrevDP[0, TileCnt] = 1;
for (long I = 1; I <= TileCnt; I++) {
long[,] CurrDP = new long[TileCnt + 1, TileCnt + 1];
for (long LeftTileCnt = 0; LeftTileCnt <= TileCnt; LeftTileCnt++) {
for (long RightTileCnt = 0; RightTileCnt <= TileCnt; RightTileCnt++) {
long CurrPetternCnt = PrevDP[LeftTileCnt, RightTileCnt];
if (CurrPetternCnt == 0) continue;
//左にジャンプする場合
if (I % 2 == 0 && LeftTileCnt > 0) {
for (long JumpedTileCnt = 0;
JumpedTileCnt <= LeftTileCnt - 1; JumpedTileCnt++) {
CurrDP[LeftTileCnt - JumpedTileCnt - 1,
RightTileCnt + JumpedTileCnt] += CurrPetternCnt;
}
}
//右にジャンプする場合
if (I % 2 == 1 && RightTileCnt > 0) {
for (long JumpedTileCnt = 0;
JumpedTileCnt <= RightTileCnt - 1; JumpedTileCnt++) {
CurrDP[LeftTileCnt + JumpedTileCnt,
RightTileCnt - JumpedTileCnt - 1] += CurrPetternCnt;
}
}
}
}
Console.WriteLine("{0}回目のDPの結果", I);
PrintDP(CurrDP);
PrevDP = CurrDP;
}
Console.WriteLine("Answer={0}", PrevDP.Cast<long>().Sum());
}
static void PrintDP(long[,] pDP)
{
var sb = new System.Text.StringBuilder();
for (long I = 0; I <= pDP.GetUpperBound(0); I++) {
for (long J = 0; J <= pDP.GetUpperBound(1); J++) {
if (pDP[I, J] == 0) continue;
sb.AppendFormat("左のタイル{0}個で右のタイル{1}個の場合の数={2}",
I, J, pDP[I, J]);
sb.AppendLine();
}
}
Console.WriteLine(sb.ToString());
}
}
ソース(ジグザグ数を求める)
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
//const int TileCnt = 6;
const int TileCnt = 20;
static void Main()
{
var ZigZagValDict = new Dictionary<int, List<long>>();
for (int N = 1; N <= TileCnt - 1; N++) {
if (N == 1) {
ZigZagValDict.Add(1, new List<long>() { 1 });
continue;
}
var WillAddList = new List<long>() { 0 };
long CurrSum = 0;
for (int I = ZigZagValDict[N - 1].Count - 1; 0 <= I; I--) {
CurrSum += ZigZagValDict[N - 1][I];
WillAddList.Add(CurrSum);
}
ZigZagValDict.Add(N, WillAddList);
}
Console.WriteLine("Answer={0}", ZigZagValDict[TileCnt - 1].Last());
}
}
実行結果
16回目のDPの結果
左のタイル0個で右のタイル2個の場合の数=1372696498127
左のタイル1個で右のタイル1個の場合の数=1032183177314
左のタイル2個で右のタイル0個の場合の数=562021682744
17回目のDPの結果
左のタイル0個で右のタイル1個の場合の数=1372696498127
左のタイル1個で右のタイル0個の場合の数=2404879675441
18回目のDPの結果
左のタイル0個で右のタイル0個の場合の数=2404879675441
Answer=2404879675441
解説
三角形を経由して、ジグザグ数を求めると計算量が少ないです。
オイラー数とタンジェント数(1)
ジグザグ数(zigzag number)
オイラー数はzig number、タンジェント数はzag numberとも呼ばれ、下図の三角形により生成される。その規則は
・頂点は1であり、0から始めて右左右左とジグザグに値を決める
・進行方向の斜め上の数を加えて次の数を作る(例:10+4=14)
この数は下図の折れ線で示す交代順列の個数に等しい。