ABC#011 C.123引き算

Haskellで挑戦したけどダメで、C++で書いても間違えるという・・・

問題

数字Nについて、1,2,3の中から好きな数値を引く操作を100回まで行える。
最終的に0にできるかどうかYES/NOで返す。
ただし、NG数字が3つあり、その数字に一時的にでもなってしまってはいけない。

考え方

Haskell再帰を書いたらメモ化の仕方がよくわからず・・・そもそもメモ化するような書き方をしない気がするけどわからない・・・


0から逆に増やす方向に、毎ターン、前ターンまでで行けてた数値から1,2,3を足した部分を行けるようにする。それを100ターン分繰り返す。300まで*100ターン=30000回程度しかループしないので、問題ない。


または、greedyに3,2,1の順序で優先的にいけるだけいって調整するようなやりかたで十分。

Haskellで解いた

import Control.Applicative
import Control.Monad
import Data.List

remove_in :: [Int] -> [Int] -> [Int]
remove_in [] ng = []
remove_in (x:xs) ng
    | x `elem` ng = remove_in xs ng
    | otherwise   = x : remove_in xs ng

add123 arr ng = arr'
    where arr' = remove_in (nub $ (map (+1) arr) ++ (map (+2) arr) ++ (map (+3) arr)) ng

make_table :: [Int] -> [Int] -> Int -> [Int]
make_table arr ng itr
    | itr < 0   = []
    | otherwise = nub $ arr ++ (make_table arr' ng (itr-1))
        where arr' = add123 arr ng

main :: IO ()
main = do
    n <- readLn
    ng <- replicateM 3 readLn
--    print $ (make_table [0] ng 100)
    putStrLn $ if n `elem` make_table [0] ng 100
        then "YES"
        else "NO"

remove_inじゃなくてfilterで書きたい。
無駄なのが多いけどうまい書き方がまだわかってない・・・

-- notElemがあった
add123 arr ng = filter (`notElem` ng) (nub $ (map (+1) arr) ++ (map (+2) arr) ++ (map (+3) arr))