这是我的代码:
static int cardGameValue(List<int> D, int myScore, int opponentScore)
{
if (D.Count == 0) return myScore;
else if (D.Count == 1)
{
opponentScore += D[0];
return myScore;
}
else
{
if (D[0] <= D[D.Count - 1])
{
opponentScore += D[D.Count - 1];
D.RemoveAt(D.Count - 1);
}
else
{
opponentScore += D[0];
D.RemoveAt(0);
}
int left = cardGameValue(new List<int>(D.GetRange(1, D.Count - 1)), myScore + D[0], opponentScore);
int right = cardGameValue(new List<int>(D.GetRange(0, D.Count - 1)), myScore + D[D.Count - 1], opponentScore);
if (left >= right)
{
return left;
}
else
{
return right;
}
}
}
}
我的代码采用了一组牌,代表了与确定对手比赛时的最高分数。在你的每个对手的比赛结束后,你有2个选择,直到所有牌都被选中。有没有办法以某种方式存储我的迭代结果,所以我可以改进我的算法?那么递归不会做不必要的迭代?因为在40或50张牌之后它变得非常慢。
答案 0 :(得分:3)
您只能访问列表D
中的第一个或最后一个元素。您可以传递完整的卡片列表(甚至更好:作为int
数组)以及第一个和最后一个位置的索引,而不是传递完整的列表。
计算结束后计算对手得分的速度要快得多:myScore
和opponentScore
加起来是卡的总和,所以你可以在O(n)中做到这一点时间。这样,您就可以删除所有更新opponentScore
的代码。
您也不需要传递myScore
。如果您让cardGameValue
返回仅从剩余卡片中获得的最佳分数。
最后,如果您使用第一个和最后一个索引,则可以将分数存储在2D数组中,并按first
和last
编制索引。如果所有牌都有正值,那么如果至少剩下两张牌,则得分必须为正。
因此,在通话开始时,您会检查缓存的分数是否为正数。如果是,您可以立即归还。如果没有,则必须计算它,然后将其存储在缓存数组中。
这就是你最终的结果:
static int cardGameValue(int[] D, int first, int last) {
scores = new int[last + 1, last + 1];
return cardGameValue(D, first, last, scores);
}
static int cardGameValue(int[] D, int first, int last, int[,] scores) {
// If we have at most 1 card, our score is 0:
if (first >= last)
return 0;
// Otherwise, get the score from the cache array.
// If it is positive, return the value.
int score = scores[first, last];
if (score > 0)
return score;
// Keep the original first and last
// for filling in the computed value later.
int firstOriginal = first;
int lastOriginal = last;
// Let the opponent pick a card:
if (D[first] <= D[last])
last--;
else
first++;
// Choose our best card:
int left = D[first] + cardGameValue(D, first + 1, last, scores);
int right = D[last] + cardGameValue(D, first, last - 1, scores);
score = Math.Max(left, right);
// and enter the score into the cache array:
scores[firstOriginal, lastOriginal] = score;
// Finally, return the computed score.
return score;
}
即使是300张卡,我的机器上运行时间不到1 毫秒。
答案 1 :(得分:2)
您可以尝试动态编程,只需将中间结果存储在一些合适的数据结构中,当您需要调用递归调用时,只需使用存储的值!
您可以使用int
的二维数组来存储结果。 [i][j]
处的元素会将游戏结果与D[i]
通过D[j]
存储在一起。从第0行开始,然后使用结果填充第1行,依此类推。这将在O(n ^ 2)时间内计算结果。
答案 2 :(得分:1)
这些行正在分配新的List对象,更不用说GetRange方法本身会创建一个新的List对象,因此每次执行时,这两行代码总共创建了4个新的List对象。如果列表中有大量项目,这可能相对昂贵。
int left = cardGameValue(new List<int>(D.GetRange(1, D.Count - 1)), myScore + D[0], opponentScore);
int right = cardGameValue(new List<int>(D.GetRange(0, D.Count - 1)), myScore + D[D.Count - 1], opponentScore);
您可以修改方法签名以获取startIndex和length参数,以便每次调用cardGameValue都可以重用相同的List实例。
static int cardGameValue(List<int> D, int startIndex, int length, int myScore, int opponentScore)
也许像这样进行递归调用:
int left = cardGameValue(D, startIndex + 1, length - 1, myScore + D[startIndex], opponentScore);
int right = cardGameValue(D, startIndex, length - 1, myScore + D[startIndex + length - 1], opponentScore);
即。引用0
索引的代码(例如D[0]
和D.RemoveAt(0)
)需要修改为使用startIndex,例如D[startIndex]
和D.RemoveAt(startIndex)
。 引用
更正:引用D.Count
的代码需要使用startIndex + length
重新提及。D.Count - 1
的代码需要替换为length - 1
或startIndex + length - 1
(取决于上下文),但仅引用D.Count
的代码只是替换为length
。