使用M种颜色和动态编程着色N段

时间:2016-07-09 05:54:16

标签: dynamic-programming

问题:我有N个连续的段,编号从1到N,M个颜色编号从1到M. 现在,有两个数字U和V定义为:

  • U =颜色(i)+颜色(j)
  • V =颜色(j)+颜色(k)
  • U,V是互质的。

其中1 <= i,j,k <= N和
j = i + 1,k = j + 1

问题是找到所有N段可以着色的方式的数量,以便上述属性适用于所有i,j,k。
是否有针对此问题的动态编程解决方案?它是什么?

1 个答案:

答案 0 :(得分:0)

我有一个递归但非[动态编程]的实现,这应该有助于让你指向正确的方向。它是在Common Lisp中实现的,因为没有指定语言。

将其扩展为动态编程解决方案的方法是添加缓存。

count-all-coprime-triple-colorings构造内存中的所有颜色,然后检查每个颜色以满足互质三重条件。

count-all-coprime-triple-colorings-lazy尝试通过排除不符合互质条件的前缀的颜色来积极地修剪我们甚至考虑的颜色。

这种方法可以通过注意只有前缀的最后两个元素是相关的来改进,所以你可以使用它来填充缓存。

(defun coprime-p (a b)
  "check whether a and b are coprime"
  (= (gcd a b) 1))

(defun coprime-triple-p (a b c)
  "check whether (a+b) and (b+c) are coprime"
  (coprime-p (+ a b) (+ b c)))

(defun coprime-triple-sequence-p (seq)
  "check whether seq is a sequence of corpime triples"
  (cond
    ;; if the length is less than 2 then
    ;; every triple is trivially coprime
    ((<= (length seq) 2) t)
    (t (let 
         ((a (nth 0 seq))
          (b (nth 1 seq))
          (c (nth 2 seq))
          (tail (cdr seq)))
         (if (coprime-triple-p a b c)
           (coprime-triple-sequence-p tail) 
          nil)))))

(defun curry-cons (x)
  "curried cons operator"
  (lambda (list) (cons x list)))

(defun all-colorings (sections colors)
  "generate all possible #colors-colorings of sections"
  (assert (>= sections 0))
  (assert (>= colors 1))
  (cond
    ;; if there are no sections
    ;; then there are no colorings
    ((= sections 0) ())
    ;; when we have one section there is one coloring
    ;; for each color
    ((= sections 1) (loop for i from 1 upto colors collecting (list i)))
    (t
      ;; wildly inefficient
      (loop for i from 1 upto colors appending
            (mapcar (curry-cons i) (all-colorings (1- sections) colors))))))

(defun count-all-coprime-triple-colorings (sections colors)
  "count all the colorings that have coprime triples"
  (loop for i in (all-colorings sections colors) counting (coprime-triple-sequence-p i)))

(defun coprime-triple-check-boundary (reversed-prefix suffix)
  "prefix = [...a, b] ; suffix = [c,...] ; check
  gcd(a+b, b+c) != 1"
  ;; if there aren't enough elements in reversed-prefix and suffix
  ;; then we admit the list
  (if (and (nth 1 reversed-prefix) (nth 0 suffix))
    (let 
      ((b (nth 0 reversed-prefix)) (a (nth 1 reversed-prefix)) (c (nth 0 suffix)))
      (coprime-triple-p a b c))
    t))

(defun count-all-coprime-triple-colorings-lazy (sections colors reversed-prefix)
  "count the number of sequences with coprime triples with a particular number
  of sections and colors with a particular reversed-prefix."
  (let
    ((sections-- (1- sections)))
    (cond
      ((= sections 0) 1) 
      (t (loop for i from 1 upto colors summing
            (if (coprime-triple-check-boundary reversed-prefix (list i))
              (count-all-coprime-triple-colorings-lazy sections-- colors (cons i reversed-prefix))
              0))))))

(defun summarize-coloring (i j)
  "summarize the given coloring number"
  (print (list "triples" i "colors" j 
               (count-all-coprime-triple-colorings-lazy i j nil))))

(loop for i from 1 upto 9 doing
      (loop for j from 1 upto 9 doing (summarize-coloring i j)))