Kotlin钱币兑换递归函数法

vs91vp4v  于 2022-11-16  发布在  Kotlin
关注(0)|答案(1)|浏览(108)

我尝试用下一个基本思想来解决leetcode problem

fun coinChange(coins: IntArray, amount: Int): Int {
    fun calc(source: Long, lvl: Int): Int =
        if (source == amount.toLong())
            lvl
        else if (source > amount)
            -1
        else
            coins
                .map { calc(source = source + it, lvl = lvl + 1) }
                .filter { it > 0 }
                .sorted()
                .firstOrNull() ?: -1

    return calc(source = 0, lvl = 0)
}

这个算法看起来是正确的,但是它非常慢,并且因为堆栈溢出而没有通过测试。在这种情况下,我试着稍微加快它的速度,但是现在它不能正常工作了:

fun coinChange(coins: IntArray, amount: Int): Int {
    val memoized = mutableMapOf<Int, Int>()

    fun calc(source: Int, lvl: Int): Int =
        if (source == amount)
            lvl
        else if (source > amount)
            -1
        else
            memoized.getOrElse(source) {
                val evaluated = coins
                    .reversed()
                    .map { calc(source = source + it, lvl = lvl + 1) }
                    .filter { it > 0 }
                    .minOrNull() ?: -1

                memoized[source] = evaluated
                evaluated
            }

    return calc(source = 0, lvl = 0)
}

对于输入coinChange(coins = intArrayOf(186, 419, 83, 408), amount = 6249),它返回36,但必须是20。您能帮助我吗?

ctehm74n

ctehm74n1#

Kotlin(Scala和Swift也是)函数式编程方面被高估了。在你的代码中:反向,Map和过滤触发3个循环。所以最好只做常规的for/while循环。每个调用触发3个循环(虽然它可以通过一个循环完成),你将会有一个很大的性能打击。我已经研究了Kotlin的记忆机制,我没有找到一个适合递归函数。(我希望被指出/教育)(这个https://stackoverflow.com/a/35309775和类似的类方法不起作用。我必须手动管理该高速缓存)好了,我的抱怨已经够多了,这里是手动管理缓存通过189 / 189测试的解决方案

fun coinChange(coins: IntArray, amount: Int): Int {
    val cache = mutableMapOf<Pair<Int, Int>, Int>()
    fun dfs(p:Pair<Int, Int>):Int{
        cache[p]?.let{return it}
        val (idx, amount) = p
        val ans = if(amount == 0) 0
        else{
            var count = Int.MAX_VALUE
            for(i in idx until coins.size){ 
                val nxt = amount - coins[i]
                if(nxt < 0) continue
                val res = dfs(i to nxt) 
                if(res == Int.MAX_VALUE) continue
                count = Math.min(count, res + 1)
            }
            count 
        }
        cache[p] = ans
        return ans
    }
    coins.sort()
    coins.reverse()
    val ans = dfs(0 to amount)
    return if(ans != Int.MAX_VALUE) ans else -1
}

这是未请求的python代码。缓存是用一个简单的decorator @cache很好地完成的

def coinChange(self, coins: List[int], amount: int) -> int:
    @cache
    def dfs(idx, target):
        if target == 0:
            return 0
        result = float('Inf')
        for i in range(idx, len(coins)):
            if target - coins[i] < 0: continue 
            result = min(result, 1 + dfs(i, target - coins[i]))
        return result
    coins.sort(reverse=True) 
    ans = dfs(0, amount)
    return -1 if ans == float('Inf') else ans

下面是另一种使用手动记忆的递归方法

fun coinChange(coins: IntArray, amount: Int): Int { 
    val MAX:Long = Int.MAX_VALUE.toLong()
    val cache = mutableMapOf<Pair<Int, Int>, Long>()
    fun dfs(p:Pair<Int, Int>):Long{
        cache[p] ?. let{return it}
        val (idx, amount) = p
        val ans =   if(amount == 0) 0
                    else if(amount < 0 || idx == coins.size) MAX
                    else Math.min(dfs(idx + 1 to amount), 1 + dfs(idx to amount - coins[idx]))
        cache[p] = ans
        return ans
    }
    val ans = dfs(0 to amount) 
    return if(ans == MAX) -1 else ans.toInt()
}

和Python计数器部分

def coinChange(self, coins: List[int], amount: int) -> int:
    @cache
    def dfs(idx, amount):
        if amount == 0: return 0
        if amount < 0 or idx == len(coins): return float('Inf')
        return min(dfs(idx + 1, amount), 1 + dfs(idx, amount - coins[idx]))
    ans = dfs(0, amount)
    return ans if ans != float('Inf') else -1

好吧,你可能仍然不满足于这些解决方案,毕竟,你想使用花哨的函数式编程听起来的东西。好吧,这是使用map的Kotlin代码,我有意识地避免了filter,因为它将需要另一个循环。

fun coinChange(coins: IntArray, amount: Int): Int {
    val MAX:Long = Int.MAX_VALUE.toLong()
    val cache = mutableMapOf<Pair<Int, Int>, Long>()
    fun dfs(p:Pair<Int, Int>):Long{
        cache[p]?.let{return it}
        val (idx, amount) = p
        val ans:Long = if(amount == 0) 0
        else{ 
            (idx until coins.size).map{ i ->
                val nxt = amount - coins[i]
                if(nxt >= 0) 1 + dfs(i to nxt) else MAX 
            }.min()?:MAX
        }
        cache[p] = ans
        return ans
    }
    coins.sort()
    coins.reverse()
    val ans = dfs(0 to amount)  
    return if(ans >= MAX) -1 else ans.toInt()
}

由于提交了更多Python解决方案,因此对Python的leetcode测试更加严格,Python等效项无法通过TLE,但您可以在此处看到在Python列表理解中组合的map、filter、min all的一行代码

def coinChange(self, coins: List[int], amount: int) -> int:
    @cache
    def dfs(idx, tgt):
        return 0 if tgt == 0 else min([1 + dfs(i, tgt - coins[i]) for i in range(idx, len(coins)) if tgt - coins[i] >= 0], default=float('inf')) 
    coins.sort(reverse=True) 
    ans = dfs(0, amount)
    return -1 if ans == float('Inf') else ans

相关问题