问题 (Un)根据图案折叠纸张并给出层的顺序


上个星期五,在法国节目比赛中,我们得到了纸张折叠问题,如下:

  • 给一张方纸和一个折叠图案,写一个 函数“fold(pattern)”将给出图层的顺序 将由于纸张的累积折叠(一半)而产生 对模式的尊重。“

这可能不是很清楚,所以让我解释一下(如果你愿意去那么远的地方帮我解决的话,有一张方便的纸可能有助于理解:))。

假设我们有一张正方形纸,我们绘制一个N * N网格,然后对其所有内部正方形进行编号。给定一个“LTRB”模式,我们将这张纸垂直折叠成两半,并将左边的部分放在右边。然后,我们将水平折叠并将顶部放在底部。然后,我们将它再次垂直折叠并将右侧部分放在左侧部分上。最后,我们将水平折叠并将底部放在顶部。这给我们留下了一张纸,大小为一个正方形和N ^ 2层。预期答案是每个原始正方形的结果顺序。

例如,如果我们在一张方形纸上绘制一个2 * 2网格,然后将每个内部正方形从1到4(从左上到右下,水平)编号,并给出模式“LT”,我们就会有发生这种情况:

fold("LT"):

 1 | 2   L  1,2  T
---|--- ==> --- ==> 2,1,3,4
 3 | 4      3,4

并使用“RB”模式,例如:

fold("RB"): 

 1 | 2   R  2,1  B
---|--- ==> --- ==> 3,4,2,1
 3 | 4      4,3

正如您可能已经猜到的那样,它基本上归结为N * N矩阵的递归变换。这是容易的部分,这是我的解决方案:

http://ideone.com/QaXGq

现在是有趣的部分。

  • 编写一个函数展开(顺序),对于给定的结果层 排序,给出产生这种排序的模式。注意 展开(折叠(“LRTB”))=>“LRTB”

然后我的大脑停止工作了一段时间,我没有时间(总共4小时还剩2小时)来提出足够快的解决方案。

我最初的想法是:

  1. 试着暴力吧。因为我们知道输入的长度是N ^ 2,所以我们可以创建一个初始矩阵并尝试所有可能的折叠,直到我们达到与输入相同的顺序。 O(4 ^ N)复杂性,不可行。

  2. 蛮力逆转。从输入开始,尝试所有展开的可能性,直到我们达到正确的初始状态。好一点,但仍然太慢。

  3. ???

有人有想法吗?


1791
2018-05-07 16:05


起源



答案:


在每一步中,您只需要知道列表的第一个元素和最后一个元素。如果第一个元素应该在最后一个元素的左侧,则保留折叠方向(同上,右上,下)。


9
2018-05-07 16:39



这确实非常有趣!感觉愚蠢之前没见过。这也是一个非常快速的解决方案。我想知道这是否也适用于折叠机制。 - Serkan Eryilmaz


答案:


在每一步中,您只需要知道列表的第一个元素和最后一个元素。如果第一个元素应该在最后一个元素的左侧,则保留折叠方向(同上,右上,下)。


9
2018-05-07 16:39



这确实非常有趣!感觉愚蠢之前没见过。这也是一个非常快速的解决方案。我想知道这是否也适用于折叠机制。 - Serkan Eryilmaz


这是我的尝试。这听起来很容易,但像往常一样,魔鬼在细节中。

第一, fold

  type Matrix = IndexedSeq[IndexedSeq[IndexedSeq[Int]]]

  def initial(folds: Int): Matrix = {
    val sideLen = math.pow(2, folds / 2).toInt
    (1 to sideLen * sideLen) map (Vector(_)) grouped sideLen toIndexedSeq
  }

  def leftFold (m: Matrix): Matrix = m map { r => 
    val (a, b) = r splitAt (r.size / 2) 
    (a.reverse, b).zipped map (_.reverse ++ _)
  }

  def rightFold(m: Matrix): Matrix = m map { r => 
    val (a, b) = r splitAt (r.size / 2) 
    (b.reverse, a).zipped map (_.reverse ++ _)
  }

  def topFold   (m: Matrix): Matrix = leftFold(m.transpose).transpose
  def bottomFold(m: Matrix): Matrix = rightFold(m.transpose).transpose

  def fold(input: String): Seq[Int] = {
    def go(in: String, m: Matrix): Seq[Int] = in match {
      case "" => m(0)(0)
      case s  => go(s.tail, s.head match {
        case 'L' => leftFold(m)
        case 'R' => rightFold(m)
        case 'T' => topFold(m)
        case 'B' => bottomFold(m)
      })
    }
    go(input, initial(input.length))
  }

第二, unfold

  def unfold(input: Seq[Int]): String = { 
    val m0: Matrix = Vector(Vector(Vector(input: _*)))
    val sideLen = math.sqrt(input.length).toInt 

    def go(m: Matrix, out: String): String = {
      val tl = m.head.head
      val bl = m.last.head
      val tr = m.head.last
      if (m.length == sideLen && m.head.length == sideLen) out.reverse
      else if (tl.head == tl.last - 1)       go(leftUnfold(m),   out + 'L')
      else if (tr.head == tr.last + 1)       go(rightUnfold(m),  out + 'R')
      else if (tl.head == tl.last - sideLen) go(topUnfold(m),    out + 'T')
      else if (bl.head == bl.last + sideLen) go(bottomUnfold(m), out + 'B')
      else sys.error("no fold found " + m) 
    }
    go(m0, "")
  }    

  def leftUnfold (m: Matrix): Matrix = m map { r =>
    val (a, b) = r.map(e => e.splitAt(e.size / 2)).unzip
    a.map(_.reverse).reverse ++ b
  }

  def rightUnfold(m: Matrix): Matrix = m map { r =>
    val (a, b) = r.map(e => e.splitAt(e.size / 2)).unzip
    b ++ a.map(_.reverse).reverse
  }

  def topUnfold   (m: Matrix): Matrix = leftUnfold (m.transpose).transpose
  def bottomUnfold(m: Matrix): Matrix = rightUnfold(m.transpose).transpose

我跑了几个测试,它通过了......


2
2018-05-08 05:12



非常优雅的解决方案,我也喜欢转置矩阵和应用垂直折叠的想法。对于你展开的解决方案,在我看来你不需要“b1”和“tr”,“tl”就足够了。 - Serkan Eryilmaz
@Serkan(tl =左上角,bl =左下角,tr =右上角)。我确实认为可能只看左上方的Vector [Int]元素,但是你需要考虑当前的水平和垂直长度来确定最后的Int应该是什么,我认为这更复杂而不仅仅是使用上面的不同角落。但是,如果你能改进这一点,我很有兴趣看到! - Luigi Plinge
ideone.com/n0bsY 对我来说很好看;) - Serkan Eryilmaz
@SerkanEryilmaz失败了 unfold(fold("RBLT")) 例如。 ideone.com/YYpkF - Luigi Plinge
@Luigi,你能解释一下这段代码背后的算法,尤其是zip和map操作吗?我不熟悉Scala .. - Pradeep