7

「4×1 および 6×1 のレゴ ブロックの列からソリッド パネルを構築するとします。構造強度のために、ブロック間のスペースが隣接する列に並んではいけません。例として、18×3 のパネルが示されています。上の 2 つの行のブロック間のスペースが並んでいるため、以下は受け入れられません。

10×1 パネルを構築するには 2 つの方法、10×2 パネルを構築するには 2 つの方法、18×3 パネルを構築するには 8 つの方法、36×5 パネルを構築するには 7958 通りの方法があります。

64×10 のパネルを構築するには、いくつの方法がありますか? 答えは 64 ビットの符号付き整数に収まります。答えを計算するプログラムを書きなさい。プログラムは非常に速く実行される必要があります。確かに、古いマシンでも 1 分以上かかることはありません。プログラムが計算する値、プログラムがその値を計算するのにかかった時間、実行したマシンの種類をお知らせください。プログラムのソース コードを添付ファイルとして含めます。"

私は最近、プログラミングのパズルを与えられ、それを解こうと頭を悩ませています。私は c++ を使用していくつかのコードを書きましたが、その数が膨大であることはわかっています... 私のプログラムは数時間実行された後、停止することにしました。これに似たパズルを見た人はいますか?数週間経ちましたが、もうこれを提出することはできませんが、正しく解決できなかったことに本当に悩まされています. 使用するアルゴリズムに関する提案はありますか? または、「箱の外」で解決する可能性のある方法かもしれません。私が頼ったのは、4x1および6x1ブロックの可能な「レイヤー」をそれぞれ構築して64x1レイヤーを作成するプログラムを作成することでした。それは約 3300 の異なるレイヤーであることが判明しました。次に、プログラムを実行して、可能なすべての 10 層の高さの壁にそれらを積み重ねて、亀裂が並んでいないようにしました...ご覧のとおり、このソリューションには長い、長い、長い時間がかかります。したがって、時間の制約内でこれを解決するには、明らかにブルートフォースは効果的ではないようです。任意の提案/洞察をいただければ幸いです。

4

3 に答える 3

4

これが私の答えです。それは Haskell です。とりわけ、ビッグナムを無料で取得できます。

編集:実際に妥当な時間内に問題を解決するようになりました。

より多くの編集: 疎行列を使用すると、コンピューターで 0.5 秒かかります。

行を並べて表示するために考えられる方法をそれぞれ計算します。行を並べて表示する方法が N 個あるとします。NxN 行列を作成します。要素 i,j は、行 i が行 j の隣に表示される場合は 1、それ以外の場合は 0 です。N 個の 1 を含むベクトルから始めます。壁の高さから 1 を引いた数だけ行列にベクトルを掛け、結果のベクトルを合計します。

module Main where
import Data.Array.Unboxed
import Data.List
import System.Environment
import Text.Printf
import qualified Data.Foldable as F
import Data.Word
import Data.Bits

-- This records the index of the holes in a bit field
type Row = Word64

-- This generates the possible rows for given block sizes and row length
genRows :: [Int] -> Int -> [Row]
genRows xs n = map (permToRow 0 1) $ concatMap comboPerms $ combos xs n
  where
    combos [] 0 = return []
    combos [] _ = [] -- failure
    combos (x:xs) n =
      do c <- [0..(n `div` x)]
         rest <- combos xs (n - x*c)
         return (if c > 0 then (x, c):rest else rest)
    comboPerms [] = return []
    comboPerms bs =
      do (b, brest) <- choose bs
         rest <- comboPerms brest
         return (b:rest)
    choose bs = map (\(x, _) -> (x, remove x bs)) bs
    remove x (bc@(y, c):bs) =
      if x == y
         then if c > 1
                 then (x, c - 1):bs
                 else bs
         else bc:(remove x bs)
    remove _ [] = error "no item to remove"
    permToRow a _ [] = a
    permToRow a _ [_] = a
    permToRow a n (c:cs) =
      permToRow (a .|. m) m cs where m = n `shiftL` c

-- Test if two rows of blocks are compatible
-- i.e. they do not have a hole in common
rowCompat :: Row -> Row -> Bool
rowCompat x y = x .&. y == 0

-- It's a sparse matrix with boolean entries
type Matrix = Array Int [Int]
type Vector = UArray Int Word64

-- Creates a matrix of row compatibilities
compatMatrix :: [Row] -> Matrix
compatMatrix rows = listArray (1, n) $ map elts [1..n] where
  elts :: Int -> [Int]
  elts i = [j | j <- [1..n], rowCompat (arows ! i) (arows ! j)]
  arows = listArray (1, n) rows :: UArray Int Row
  n = length rows

-- Multiply matrix by vector, O(N^2)
mulMatVec :: Matrix -> Vector -> Vector
mulMatVec m v = array (bounds v)
    [(i, sum [v ! j | j <- m ! i]) | i <- [1..n]]
  where n = snd $ bounds v

initVec :: Int -> Vector
initVec n = array (1, n) $ zip [1..n] (repeat 1)

main = do
  args <- getArgs
  if length args < 3
    then putStrLn "usage: blocks WIDTH HEIGHT [BLOCKSIZE...]"
    else do
      let (width:height:sizes) = map read args :: [Int]
      printf "Width: %i\nHeight %i\nBlock lengths: %s\n" width height
             $ intercalate ", " $ map show sizes
      let rows = genRows sizes width
      let rowc = length rows
      printf "Row tilings: %i\n" rowc
      if null rows
        then return ()
        else do
          let m = compatMatrix rows
          printf "Matrix density: %i/%i\n"
                 (sum (map length (elems m))) (rowc^2)
          printf "Wall tilings: %i\n" $ sum $ elems
                  $ iterate (mulMatVec m) (initVec (length rows))
                            !! (height - 1)

そして結果は…

$ time ./a.out 64 10 4 6
Width: 64
Height 10
Block lengths: 4, 6
Row tilings: 3329
Matrix density: 37120/11082241
Wall tilings: 806844323190414

real    0m0.451s
user    0m0.423s
sys     0m0.012s

よし、500 ミリ秒、私はそれで我慢できます。

于 2009-05-29T10:35:48.040 に答える
1

さまざまな形のタイルで長い廊下をタイル張りするプログラミング コンテストの同様の問題を解決しました。私は動的プログラミングを使用しました。任意のパネルが与えられた場合、一度に 1 つの行を配置することによってそれを構築する方法があります。各行は、その最後に有限数の形状を持つことができます。したがって、行の数ごと、形状ごとに、その行を作成する方法がいくつあるかを計算します。(一番下の行については、各形状を作成する方法が 1 つだけあります。) 次に、各行の形状によって、次の行が取ることができる形状の数が決まります (つまり、スペースを並べることはありません)。この数は行ごとに有限であり、実際にはレンガのサイズが 2 つしかないため、小さくなります。したがって、行ごとに一定の時間を費やすことになり、プログラムはすぐに終了します。

形状を表すには、4 と 6 のリストを作成し、このリストをテーブルのキーとして使用して、各iについてその形状を行iに作成する方法の数を格納します。

于 2009-05-27T02:58:51.750 に答える