我有一个有效的递归函数用于分配。但是,部分要求是通过使用备忘录降低复杂性。
该功能类似于移动点击游戏。用户可以执行a)点击b)升级。该功能找到最少的抽头次数以达到指定的数量。用户可以跳过级别-从而绕过该级别的费用。
例如:
Levels: 0, 1, 2.
Cost of upgrades: 0, 5, 8.
Money per tap: 2, 4, 9.
其中一条路线,所需金额为$ 15:
总共3 + 2 + 2 = 7次点击。
一条更有效的路线将是:
升级为$ 8,跳过第二级升级。 (还剩$ 0。)
点击两次以达到所需金额($ 18> $ 15。)
总共4 + 2 = 6次抽头。
现在我有一个适用于上述情况的递归程序。我尝试通过使用2D数组cache[levels][amount]
来记住程序,在其中存储每个级别/金额的水龙头,并在需要时进行访问。
最小的工作解决方案如下:
import java.io.*;
import java.math.*;
import java.util.*;
class IdleGame {
private static int fork(int current, int required, int level, int taps, int[] values, int[] costs, int maxLevel,
int[][] cache) {
int first = Integer.MAX_VALUE;
int second = Integer.MAX_VALUE;
if (current >= required) {
return taps;
}
//upgrade path, call recursively if available.
for (int i = level + 1; i <= maxLevel; i++) {
if (current >= costs[i]) {
if (cache[i][current] == 0) {
first = fork(current - costs[i], required, i, taps, values, costs, maxLevel, cache);
cache[i][current] = first;
} else {
// System.out.println("First");
first = cache[i][current];
}
}
}
if (cache[level][current] == 0) {
second = fork(current + values[level], required, level, taps + 1, values, costs, maxLevel, cache);
cache[level][current] = second;
} else {
// System.out.println("Second");
second = cache[level][current]--;
}
return first < second ? first : second;
};
public static void main(String[] args) {
int[] values = {2,4,9};
int[] costs = {0,5,8};
int[][] cache = new int[3][16];
int solution = fork(0, 15, 0, 0, values, costs, 2, cache);
System.out.println(solution);
}
}
但是,对于我的测试案例而言,这种解决方案还不够快。据我所知,当递归函数的另一个实例调用相同的level/value
时,我会使用记忆值,这是在两个递归函数相交(即具有相同参数)时发生的。
任何指导将不胜感激。
答案 0 :(得分:4)
我看不到为什么只存储second
而不存储first
的原因。实际上,您应该只存储最终结果并在方法开始时进行检查:
private static int fork(int current, int required, int level, int taps, int[] values, int[] costs, int maxLevel, int[][] cache) {
if (current >= required)
return taps;
// check cache, calculate value only if cache is empty
if (cache[level][current] == 0) {
// calculate first value
int first = Integer.MAX_VALUE;
for (int i = level + 1; i <= maxLevel; i++) {
if (current >= costs[i]) {
first = fork(current - costs[i], required, i, taps, values, costs, maxLevel, cache);
}
}
// calculate second value
int second = fork(current + values[level], required, level, taps + 1, values, costs, maxLevel, cache);
// store result in cache
cache[level][current] = first < second ? first : second;
}
// return cached value
return cache[level][current];
}
顺便说一句,通常通过检查值是否为0来检查是否设置了高速缓存。如果有效结果实际上可以为0怎么办?最好使用可为null的类型或可以检查是否存在键的容器。
我注意到的另一件事是,由于您没有中断条件,因此根据您的输入,您将在该循环中反复覆盖first
。因此,您为每个小于first
的{{1}}重新计算costs[i]
。您应该确保找到所需的 one current
,并仅为此计算costs[i]
。如果您只想查找小于first
的前costs[i]
,只需添加一个current
:
break
如果要查找小于for (int i = level + 1; i <= maxLevel; i++) {
if (current >= costs[i]) {
first = fork(current - costs[i], required, i, taps, values, costs, maxLevel, cache);
break;
}
}
的最小costs[i]
,则需要存储索引并在循环后调用current
:
fork
===
请注意,您的代码有些混乱,可以使用一些良好的重构来提高可读性。例如,您可能想要创建一个用于保存基本参数的类,并将其实例传递给// find smallest costs[i] smaller than current:
int smallestCostIndex = -1;
int smallestCost = Integer.MAX_VALUE;
for (int i = level + 1; i <= maxLevel; i++) {
if (current >= costs[i] && costs[i] < smallestCost) {
smallestCost = costs[i];
smallestCostIndex = i;
}
}
// calculate first using smallest costs[i] smaller than current (if it exists):
int first = Integer.MAX_VALUE;
if (smallestCostIndex >= 0) {
first = fork(current - costs[i], required, i, taps, values, costs, maxLevel, cache);
}
。另外,适当的集合可能比简单数组更好。而且由于这些集合(数组)始终是相同的实例,因此您无需将它们作为参数传递。使它们成为您的类的成员,并使fork为非静态方法。像这样:
fork
最后一点,我只是将其键入下来,作为某种形式的OOP方法指导您的代码。