Project Euler 32

http://projecteuler.net/index.php?section=problems&id=32


重複は数えないからsetを使う。そして和を取る。iterableでsetを引数に取れるようにしたが、よく考えたらaccumulateを使えばいいだけだった。これがnumericにあることをいつも忘れる。

#include <iostream>
#include <set>
#include <numeric>
#include "itertools.h"

using namespace std;
using namespace itertools;

int pow(int n, int e) {
    return e == 0 ? 1 : n * pow(n, e - 1);
}

const int   N = 9;
const int   M = (N + 1) / 2;    // 左辺の合計の桁数

// get<k>(x)のkを実行時に変更するため
int get(int k, tuple<int,int,int> x) {
    return k == 0 ? get<0>(x) : (k == 1 ? get<1>(x) : get<2>(x));
}

bool is_valid(tuple<int,int,int> x) {
    int n = get<2>(x);
    // ビットを立てる
    return pow(10, N - M - 1) <= n && n < pow(10, N - M)
            && reduce([] (int x, int d) { return x | (1 << d); },
                map(snd<int,int>,
                product(range<>(3),
                        [&x] (int k) { return digits(get(k, x)); }))
                        , 0) == (2 << N) - 2;
}

vector<int> list_pandigital(int n) {
    return list(map([] (tuple<int,int,int> x) { return get<2>(x); },
            filter(is_valid,
            map([] (tuple<int,int> x) {
                    return make_tuple(fst(x), snd(x), fst(x) * snd(x)); },
            product(range<>(pow(10, n - 1), pow(10, n)),
                    range<>(pow(10, M - 1 - n), pow(10, M - n)))))));
}

int main() {
    set<int>    s;
    for(int n = 1; n <= M / 2; n++)
        iter([&s] (int m) { s.insert(m); }, iterable(list_pandigital(n)));
    cout << accumulate(s.begin(), s.end(), 0) << endl;
}