クイズ: Graphical Partition

http://mput.dip.jp/mput/?date=20051007#p01
Concurrent Cleanで解いてみた。
Haskellの回答をいろいろ見てたら、choiceとかcombとかお決まりのパターンがあるんだね。
http://www.sampou.org/cgi-bin/haskell.cgi?Programming%3a%b6%cc%bc%ea%c8%a2%3a%c1%c8%b9%e7%a4%bb
あと、「or []」は False になるので、case は不要でした。
なので、それを使ってみたら、かなり短くなった。

module graph3

import StdEnv

/** イディオム **/
choice  :: Int [a] -> [([a], [a])]
choice 0 xs = [([], xs)]
choice n [] = []
choice n [x:xs] = [([x:ys], zs) \\ (ys, zs) <- (choice (n-1) xs)] ++
                  [(ys, [x:zs]) \\ (ys, zs) <- (choice n xs)]


problem =
    //[1, 2, 2, 3, 4]       // True
    [5, 2, 3, 2, 3, 2, 5] // True
    //[4, 4, 4, 2, 2]       // False
    //[1, 1, 4]             // False

Start
    = isGraphical problem


isGraphical :: [Int] -> Bool
isGraphical []
    = True
isGraphical graph
    = or (map isGraphical next)
where
    next = map (\ (g1, g2) = g2 ++ (map (\x = x - 1) g1)) (choice (hd graph) (tl graph))
      • -

(最初の回答)
Concurrent Cleanで解いてみた。なんか、無駄に長いなぁ・・・。なんでこんなに長いんだろ。
最初、単純無向グラフの意味を取り違えて、多重辺の存在チェックを忘れていて、はまってしまいました。

module graph

import StdEnv

::Graph :== [Int]
::Edge :== (Int, Int)
::Problem :== (Graph, [Edge])

problem =
    //[1, 2, 2, 3, 4]       // True
    [5, 2, 3, 2, 3, 2, 5] // True
    //[4, 4, 4, 2, 2]       // False
    //[1, 1, 4]             // False

Start
    = isGraphical (problem, [])


instance == Edge
where
    (==) (a, b) (c, d)
        | a == c && b == d
            = True
        | a == d && b == c
            = True
        = False

isGraphical :: Problem -> Bool
isGraphical ([], _)
    = False
isGraphical (graph, edges)
    | top >= length graph
        = True
    = case next of
        [] = False
        _  = or (map isGraphical next)
where
    next = nextProblems [] (decrementGraph graph top, edges) top 1
    top  = firstNonZeroNode graph

nextProblems :: [Problem] Problem Int Int -> [Problem]
nextProblems res problem top pos
    | pos >= length graph
        = res
    | top == pos || isMember (top, pos) edges || graph!!pos == 0
        = nextProblems res problem top (pos+1)
        = nextProblems [(nextGraph, [(top, pos):edges]):res] problem top (pos+1)
where
    (graph, edges) = problem
    nextGraph = decrementGraph graph pos

decrementGraph :: Graph Int -> Graph
decrementGraph graph pos
    = (take pos graph) ++ [graph!!pos - 1] ++ (drop (pos+1) graph)

firstNonZeroNode :: Graph -> Int
firstNonZeroNode graph
    = fst (foldl f (0,True) graph)
where
    f (x,False) _ = (x, False)
    f (x,True ) 0 = (x + 1, True)
    f (x,True ) y = (x, False)
      • -

(2つめの回答)
よく考えたら、これでいいことが分かった。最初のアイデアをちょっと修正するだけでよかったらしい。

module graph2

import StdEnv

problem =
    //[1, 2, 2, 3, 4]       // True
    [5, 2, 3, 2, 3, 2, 5] // True
    //[4, 4, 4, 2, 2]       // False
    //[1, 1, 4]             // False

Start
    = isGraphical problem


isGraphical :: [Int] -> Bool
isGraphical []
    = True
isGraphical graph
    = case next of
        [] = False
        _  = or (map isGraphical next)
where
    next = nextGraphs (hd graph) (tl graph)

nextGraphs :: Int [Int] -> [[Int]]
nextGraphs n1 ns
    | n1 >  length ns
        = []
    = map (\edges = map (\(x,y) = x - y) (zip (ns,edges))) (makeEdge [] n1 (length ns))
where
    makeEdge :: [Int] Int Int -> [[Int]]
    makeEdge res 0 0
        = [res]
    makeEdge res cnt len
        | cnt > len
            = []
        = edge1 ++ edge2
    where
        edge1
            | cnt > 0
                = makeEdge [1:res] (cnt-1) (len-1)
                = []
        edge2 = makeEdge [0:res] cnt (len-1)