wvogel日記

自分用の技術備忘録が多めです.

Accepted!

昨日考えていた問題を解くことが出来ました!

問題はこちら

両隣との差が1となるように円を作れ、という問題
差が1で且つ、一番小さい値の両隣は常に自身より1大きい値だよなあ、と、ここまでは気づいたのですが、
どうやったらそれを落とし込めるかでつまづいていました。

今日部活中に周囲に話してみたら、先輩が答えを見つけてくれました!
個数に注目し、必要な個数の差をとっていく方法です。

例えば、テストデータで

1 2 3 2

と与えられたとき、それを小さい順に並べ、そのそれぞれの個数をとってやると、

1 2 1

となります。
一番小さい値の隣にくる数は必ず自身よりⅠ大きい数数なので、差をとってやると

1 2 1
------
0 1 1

となる。
これで、次に小さい値は2です。
1は今の操作で除去したので、2の隣には今度は3が来ます
なので今度はこれの差をとります

0 1 1
------
0 0 0

となり、全ての数が過不足なく使用されることが分かります。
この様に、全ての差をとったあと0になれば、円を作ることができるのです

これをコードに落としたのが以下

import Data.List

type Counter = Int
type Value = Int

dataList :: [String] -> [(Value,Counter)]
dataList = map f .group.sort.map read
  where
    f as = (head as,length as)

judge :: [(Value,Counter)] -> Bool
judge xs = let ys = map snd xs
           in check xs &&
               myfoldl (head ys) (tail ys)

myfoldl :: Counter -> [Counter] -> Bool
myfoldl x [y] = y-x == 0
myfoldl x (y:xs) = if y-x <= 0 then False
                               else myfoldl (y-x) xs

check :: [(Value,Counter)] -> Bool
check [_] = True
check (x:y:xs) = if abs (fst x - fst y) == 1
                        then check (y:xs)
                        else False

main = do input <- getContents
          let (n:ls) = words input
          putStrLn.ans $ judge $ dataList$ take (read n) ls
  where
    ans True  = "YES"
    ans False = "NO"

初め、myfoldlを定義せずfoldlを使っていたのですが、
それでは、上で示した計算の途中で負の値が出てきたときにそれを回収できませんでした。

また、check関数によって、
与えられた全ての数字列が1以下の差で繋がっているかをチェックしています。