ScalaでProject Euler(103)

Problem 68

まず外の5つを決めます。その和をsext、内側の和をsintとします。

sext + sint = 55

です。1列の和をslineとします。全ての列の和を取ると、内側は2回ずつカウントするので、

5sline = sext + 2sint
sint = 5(sline - 11)

sintは5の倍数、sextも5の倍数です。そうなるように外側を選びます。外側のトップだけ最小のものを選んで残りは順列を出します。内側はトップだけ決めれば、あとは先ほどの式でslineが決まっているので、残りの内側は自動的に決まります。

この問題は組みにくいですね。Arrayを使ってごまかしています。

// List(1, 2, 3) -> (1, List(2, 3)), (2, List(3)), (3, List())
def removes[T](a :List[T]) :Iterator[(T,List[T])] = a match {
    case Nil => Iterator()
    case h :: t => Iterator((h, t)) ++ removes(t)
}

// List(1, 2, 3, 4), List(2, 3) -> List(1, 4)
def sub[T](a :List[T], b :List[T]) :List[T] = (a, b) match {
    case (Nil, b) => Nil
    case (a, Nil) => a
    case (h1 :: t1, h2 :: t2) if h1 == h2 => sub(t1, t2)
    case (h1 :: t1, h2 :: t2) => h1 :: sub(t1, b)
}

// 1, 2, 3 -> 123
def number(it :Iterator[Int]) :Long =
    it.foldLeft(0L)((x, y) => x * 10 + y)

// 123 -> 1, 2, 3
def digits(n :Int) :Iterator[Int] =
    if(n == 0) Iterator() else digits(n / 10) ++ Iterator(n % 10)

def solutions(N :Int) :Iterator[Long] = {
    val M = N * 2
    val a = new Array[Int](M)
    
    def select_ext_numbers(ns :List[Int]) :Iterator[Long] =
        for(ext_ns <- ns.combinations(N) if ext_ns.sum % N == 0;
            s = (M + 1) * 2 - ext_ns.sum / N;
            ext_ns2 <- ext_ns.tail.permutations.map(ext_ns.head :: _);
            flag = ext_ns2.zipWithIndex.foldLeft(0)(set_flag);
            n <- select_int_top(sub(ns, ext_ns2), flag, s))
                yield n
    
    def select_int_top(int_ns :List[Int], flag0 :Int, s :Int) :Iterator[Long] =
        for(n <- int_ns.toIterator if (flag0 & (1 << n)) == 0;
            flag = set_flag(flag0, (n, N)) if is_satisfied(N + 1, flag, s))
                yield array_to_number(a)
    
    def is_satisfied(pos :Int, flag0 :Int, s :Int) :Boolean =
        if(pos == M)
            true
        else {
            val n = s - a(pos-N-1) - a(pos-1)
            (flag0 & (1 << n)) == 0 && 1 <= n && n < M &&
                is_satisfied(pos + 1, set_flag(flag0, (n, pos)), s)
        }
    
    def set_flag(f :Int, x :(Int,Int)) = {
        val (n, pos) = x
        a(pos) = n
        f | (1 << n)
    }
    
    def array_to_number(a :Array[Int]) :Long = {
        val it = for(k <- Iterator.range(0, N);
                     n <- Iterator(a(k), a(k+N), a((k+1)%N+N));
                     d <- digits(n))
                        yield d
        number(it)
    }
    
    select_ext_numbers(List.range(1, M + 1))
}

val N = 5
val L = 1e16.toLong
println (solutions(N).filter(L >).max)