All pastes #1609553 Raw Edit

Bucks.hs

public text v1 · immutable
#1609553 ·published 2009-10-10 10:28 UTC
rendered paste body
module Main where

import Data.List hiding (insert)
import Data.Maybe
import Data.Set (Set, empty, member, insert)
import System.Environment

type Capacities = [Int]
type Amounts = [Int]
type Move = (Int, Int)
type Node = (Amounts, [Move])
type AmountsCache = Set Amounts

main :: IO ()
main = do (target:capacity) <- fmap (map read) $ getArgs
          putStrLn $ showAnswer $ solve target capacity

solve :: Int -> Capacities -> Maybe [Move]
solve target capacity@(full:empties) =
  if canBeSolved then bfs target capacity empty [initialNode] else Nothing
  where initialNode = (full:replicate (length capacity - 1) 0, [])
        canBeSolved = target <= full && mod target (foldl1 gcd capacity) == 0

bfs :: Int -> Capacities -> AmountsCache -> [Node] -> Maybe [Move]
bfs _ _ _ [] = Nothing
bfs target capacity cache nodes = case find isAnswer nodes of
  Just node -> Just (snd node)
  Nothing -> let (nodes', cache') = reduceNodes cache [] $ map transfer $ filter canTransfer candidates in
             bfs target capacity cache' nodes'

  where isAnswer (amounts, _) = elem target amounts
        candidates = [(n, i, j) | n <- nodes, i <- [0..(numBuckets - 1)], j <- [0..(numBuckets - 1)], i /= j]
        numBuckets = length capacity
        canTransfer ((amounts, _), i, j) = amounts!!i /= 0 && capacity!!j /= amounts!!j
        transfer ((amounts, moves), i, j) = (set i iAmount $ set j jAmount amounts, (i, j):moves)
          where (transAmount, iAmount, jAmount) = (min (amounts!!i) (capacity!!j - amounts!!j), amounts!!i - transAmount, amounts!!j + transAmount)
                set i x amounts = let (hd,(_:tl)) = splitAt i amounts in hd ++ (x:tl)

reduceNodes :: AmountsCache -> [Node] -> [Node] -> ([Node], AmountsCache)
reduceNodes cache nodes [] = (nodes, cache)
reduceNodes cache nodes ((n@(amounts, move)):rest) =
  if member amounts cache then reduceNodes cache nodes rest
                          else reduceNodes (insert amounts cache) (n:nodes) rest

showAnswer :: Maybe [Move] -> String
showAnswer Nothing = "No Answer"
showAnswer (Just moves) = show (length moves) ++ " moves:" ++ (concat $ map showMove moves)
  where showMove (i, j) = " " ++ show i ++ "->" ++ show j