【问题标题】:Using dynamic programming with Scala to solve the Mixture from CodeChef使用 Scala 动态编程解决 CodeChef 中的 Mixture
【发布时间】:2012-05-08 04:25:32
【问题描述】:

我正在尝试使用 scala 解决以下codechef problem。问题陈述如下:

哈利波特面前有 n 个混合物,排成一排。每个 混合物有 100 种不同颜色中的一种(颜色的数字从 0 到 99)。

他想将所有这些混合物混合在一起。每一步,他都在走 取两种并排放置的混合物并将它们混合 放在一起,然后将得到的混合物放在它们的位置。

当混合颜色 a 和 b 的两种混合物时,所得混合物将 有颜色 (a+b) mod 100。

另外,在这个过程中会有一些烟雾。烟雾量 混合两种颜色 a 和 b 时生成的是 a*b。

找出哈利在什么时候能得到的最少烟雾量是多少 将所有混合物混合在一起。

提供的示例答案如下:

输入:

2 
18 
19 
3
40
60
20

输出:

342
2400

在第二个测试用例中,有两种可能:

first mix 40 and 60 (smoke: 2400), getting 0, then mix 0 and 20 (smoke: 0); total amount of smoke is 2400
first mix 60 and 20 (smoke: 1200), getting 80, then mix 40 and 80 (smoke: 3200); total amount of smoke is 4400

第一种情况是正确的方法,因为它可以最大限度地减少 产生的烟雾量。

我知道这可以使用动态编程来解决,但我无法解决这个问题并在 scala 中表达算法。

这就是我对这个问题的看法,在某种查找结构中(数组,以 Tuple(Int,Int) 为键的映射)存储混合两种颜色的所有计算值

这可以通过以下伪代码来完成:

for(colors1<-1 through n)
   for(colors2<-k through n)
      if(color1 != color2)
          //Check if color1,color2 combination exists as (color1,color2) or (color2,color1)    
          Map(color1,color2) = (color1+color2)%100

计算完所有初始颜色后,现在我们需要考虑混合颜色的顺序,同时还要考虑产生的烟雾。这是我遇到问题的地方。我正在尝试制定能够产生最少烟雾的子结构。

很高兴在这里获得一些指导。

谢谢

【问题讨论】:

  • 即使我已经开始觉得,与更频繁地使用具有可变数据结构的循环的命令式语言相比,用 Scala 为动态编程问题建模有点困难。

标签: algorithm scala optimization dynamic-programming


【解决方案1】:

我编写了以下动态编程解决方案。它也可以作为gist 使用。

/** Find the minimum amount of smoke (second) and resulting color (first) 
    by splitting the sequence at every possible position,
    given `lookup` contains the best mix for subsequence. */
def minSmokeMixtureSingle(s: Seq[Int], lookup: Map[Seq[Int],(Int,Int)])
  : (Int,Int) = 
    if(s.size == 1)
        (s(0),0)
    else if(s.size == 2)
        mix(s(0),s(1))
    else {
        val splits = (1 to (s.size - 1)).map(s.splitAt)
        val mixes = splits.map{ case (s1,s2) => 
            val (c1,sm1) = lookup(s1)
            val (c2,sm2) = lookup(s2)
            val (newColor,newSmoke) = mix(c1,c2)
            (newColor, newSmoke + sm1 + sm2)
        }
        mixes.minBy(_._2)
    }

def mix(m1: Int, m2: Int): (Int,Int) = ((m1+m2) % 100, m1*m2)

def minSmokeMixture(s: Seq[Int]) = {
    //create the mixture sequences with increasing length
    val windows = for(windowSize <- 1 to s.size;
        window <- s.sliding(windowSize) ) yield window
    //go over the sequences and compute the lookup-table
    val lookup = windows.foldLeft(Map.empty[Seq[Int],(Int,Int)]){
        case (lu,seq) => lu + (seq -> minSmokeMixtureSingle(seq,lu))
    }
    //simply lookup the result
    lookup(s)
}

println(minSmokeMixture(Seq(18, 19)))
println(minSmokeMixture(Seq(40, 60, 20)))

这在风格方面当然可以改进。

它为给定的示例产生正确的输出(第二个数字是烟雾,第一个是最终颜色):

(37,342)
(20,2400)

【讨论】:

  • 非常感谢。我仍在考虑一些 scala 语法,但在我看来这似乎是解决问题的正确方法。
  • @sc_ray 随时在这里提问。代码中有很多内容。
【解决方案2】:

我认为动态编程与它没有太大关系。我认为这是一个图问题,通过广度优先搜索解决。一个直接的shortest path 问题。

【讨论】:

  • 我在这里看不到图形搜索。那么你得到的复杂度是多少?我的算法最多有O(n^3)。我懒得去琢磨是不是更低了。
  • @ziggystar O(|E| + |V|log|V|),所以我们需要知道可以有多少个顶点。最排序的路径最多可以有n - 1 顶点,但我认为顶点的实际数量是指数的。边的数量受顶点数量的限制。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-06-14
  • 2023-04-01
  • 2021-10-16
  • 1970-01-01
  • 2017-12-29
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多