3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

問題

DP

ははーん、これはあれですね。
$cnt[0\leq k \leq 3][0 \leq r \leq 6]$ を、「カードを $k$ 枚選んでその和を7で割った余りが $r$ になる組み合わせの個数」としてDPして、最後に $cnt[3][0]$ を返せばいい感じですね?
カードを1枚も見ていない初期状態では $cnt[0][0] = 1, cnt[k][r] = 0$ として、カード $a_i$ に対して $cnt[k][r]$ を $cnt[k+1][(r + a_i) \bmod 7]$ に足し込めばできて、これは28要素の配列の21要素を更新するので、immutableな配列のままやってもオーバーヘッドは気にならないはず。

import Data.Array

solve :: Int   -- N
      -> [Int] -- a_i
      -> Int   -- 答え
solve _n as = stN ! (3,0)
  where
    st0 = accumArray (+) 0 ((0,0),(3,6)) [((0,0),1)]
    stN = foldl step st0 as
    step st a = accum (+) st
                [ ((succ k, j), st ! (k, i))
                | (i, j) <- [(i, mod (a + i) 7) | i <- [0 .. 6]]
                , k <- [0 .. 2] ]

できました!

公式解説

「カードの内訳が、7で割って$r$余るようなカードはそれぞれ何枚なのかだけ数えて、それらから3枚選んで7で割り切れるようにする場合の数を数えるだけだよ」

DPいらんかった。

solve :: Int -> [Int] -> Int
solve _n as = flip div 6 $ sum
  [ nI * nJ * nK
  | (i, nI) <- assocs cnt , nI > 0, let cntI = accum (+) cnt  [(i, -1)]
  , (j, nJ) <- assocs cntI, nJ > 0, let cntJ = accum (+) cntI [(j, -1)]
  , let ij = mod (i + j) 7, let k = if ij == 0 then 0 else 7 - ij
  , let nK = cntJ ! k, nK > 0
  ]
  where
    cnt = accumArray (+) 0 (0,6) [(mod a 7, 1) | a <- as]

調べる場合の数はたった $7^3$ 通りだし、カードの枚数配列は要素数たったの7なので、immutableな更新を平気で使う。
カードの選び方に順番は関係ないので、順番を気にせず数え上げた結果を $_3 P_3 = 6$ で割っている。

公式解説だと、同じ枠から選んだ場合は、とかを $_n C_r$ を駆使して数えていたり、事前計算したリストを使ったりしてるけど、単純にこれでいける。

力こそパワー解

こちらの記事に、どストレートな解が。

「何も考えずにカードリストから3枚選ぶやり方を全て試して、7で割り切れるものを数えるよ」

$_{10^5} C_3 = 166,661,666,700,000$ 通りの数え上げは無理じゃね?と思いつつ、追試。
(元記事だと最初に mod 7 を計算しているけど、それは不要)

solve :: Int -> [Int] -> Int
solve _n as = length $ filter (\x -> 0 == mod (sum x) 7) $ combinations 3 as

combinations :: Int -> [a] -> [[a]]
combinations 0 _  = [[]]
combinations _ [] = []
combinations n (x:xs) = [x:ys | ys <- combinations (pred n) xs] ++ combinations n xs

間に合っちゃいました。何が起きてるの?
問題文には $N \leq 100000$ と書いてあるけど、そんなテストケースはないとかそんな感じ?

終わりに

スキルチェックでHaskell使えるようにしてホスイ…

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?