贪婪算法最大化得分



我正试图在java中实现一个方法,该方法将具有截止日期、分数和对象编号的对象的ArrayList作为输入。每个都可以访问为:

game.get(i).number
game.get(i).score
game.get(i).deadline

其中game是ArrayList,i是索引。

我想在考虑每场比赛的截止日期的同时,最大限度地提高我的得分。每场比赛都必须在截止日期前完成。例如,如果截止日期是5,则必须在4或更早完成。

例如,这里有一个示例ArrayList,它的相应数据按分数的降序排列:

Index     Game number     Score     Deadline
0         3               76        3
1         4               66        2
2         1               52        2
3         6               51        4
4         5               47        2
5         2               23        1

那么结果可能是以下ArrayList:

maximizedScore = [4,1,3,6]->这里每个索引表示小时。尽管第3场比赛的得分比第4场和第1场比赛高,但我们将第4场比赛放在索引0中,将第1场游戏放在索引1中,因为它们的截止日期都是2,所以必须在此之前完成。然而,第三场比赛的截止日期是3,所以我们可以在第二个小时进行,以最大限度地提高总比分。请注意,第5场比赛的截止日期与第4场和第1场比赛相同,但它的分数比两者都少,所以我们牺牲了它

编辑:每个索引(0,1,2,3,4,..)表示小时,而索引处的值表示该小时完成的游戏编号。

编辑2:我们每小时只能玩一场游戏。因此,如果数据中的最新截止日期是x,那么我们最多可以玩x场游戏。即本例中的4。

这提供了245分的最高分数。

只要在截止日期之前,游戏的顺序就无关紧要。例如:[1,4,3,6]也是可以接受的,因为游戏4和游戏1都在截止日期之前完成。

不幸的是,我不知道如何去实现这一点。希望我能清楚地知道我需要什么。

感谢您的帮助!

我将尝试在不编写任何代码的情况下给出一种通用方法。

我会把你正在寻找的结果称为";时间表";。正如你在问题中所解释的,你可以在你的时间表中安排的最大游戏数量由";截止日期";列。我将称之为";时间限制";。

你在要求一个贪婪的算法。这样的算法将生成并检查所有游戏时间表,以找到能使你的分数最大化的时间表。

基本方法

你能采取的最基本的方法是生成所有长度游戏的排列";"时限";或更少。对于其中的每一个,你都会检查:

  • 这真的是一个有效的时间表吗(暂定时间表中的每一场比赛都在比赛开始时支付吗?)
  • 这个暂定时间表的总分是否高于迄今为止获得的最佳分数?如果是这样的话;保持";此解决方案并继续尝试其他计划

第一种方法的主要困难是正确生成所有可能的时间表。I.E:

  • 0
  • 0 1
  • 0 1 2
  • 0 1 2 3
  • 0 1 2 3 4
  • 0 1 2 4
  • 0 1 2 4 3
  • 0 1 3
  • 1
  • 1 0
  • 1 0 2

优化

现在,上面的基本方法有一些不足之处。例如,当你正在构建可能的时间表,而由于截止日期标准,你构建了一个不可能的时间表。例如:

  • 3 5

是一个不可能的时间表;5〃;截止日期为1(这意味着它只能作为第一场比赛进行,或者不能全部进行)。如果你能认识到这一点,那么你就会意识到,任何以3 5(3 5 0 ...3 5 1 ...)开头的时间表都是不可能的。

如果您以巧妙的顺序生成时间表,并跳过您知道不可能生成的时间表,则可以利用这一事实。

算法提示

我建议以递归方式生成时间表。您的所有游戏ID(0、1等)都在一个集合中。你从最低的索引开始:0,将其从游戏集合中删除,并将其放在你的暂定时间表中:

时间表:0

你检查它是否是一个有效的时间表(即,你刚才放在时间表中的游戏是否可以在那个时候玩)。让我们假设情况确实如此(在这种特定情况下,因为这是时间表上的第一场比赛,所以应该总是可能的)。你还可以检查一下这个暂定的时间表是否比你迄今为止发现的任何事情都要好。

然后,您尝试在您的剩余游戏集合中添加剩余的最低游戏。在这种情况下,1。同样的事情,你从游戏集中删除1,并将其放在你的时间表上:

时间表:0 1

同样的事情,你检查这是否是一个有效的时间表。你已经知道游戏0可以先玩,你检查游戏1是否可以在第二个位置玩。运气不好,游戏1的时间限制阻止了你去做。不值得再去探索以0 1开始的时间表了。你从你的时间表中删除了1,但你没有在游戏集合中替换它:因为不可能在第二位置比赛,所以不值得进一步检查。相反,我会把它放在一系列游戏中;排除";以目前的勘探水平。

然后转到游戏集合中剩余的下一个游戏2,并遵循相同的例程。如果您能够在您的时间表上放置与时间限制兼容的游戏,则可以继续将游戏添加到您的时间表中,并保留在游戏集合中。请注意,在某些情况下,你可能会完全填满你的时间表,但还有比赛要打(反之亦然,你的时间表上仍然有漏洞,但没有比赛可以打)。

当你的选项用完时,是时候从时间表中删除游戏了。删除您安排的最后一个游戏,并将其放回游戏集合中。同时将您在当前级别排除的游戏放回游戏集合中。您可以继续生成计划。注意不要再拿你刚刚在这个级别删除的游戏。

首先要做的是根据分数对游戏进行排序,因为我们首先想要一个分数最高的游戏:

如果你有一个对象ArrayList,比如ArrayList<Game> inputData = new ArrayList<>();,你可以在java:中这样排序

inputData.sort(Comparator.comparing(Game::getScore));

现在最低的分数是第一,最高的分数是最后。

由于我们必须跟踪截止日期,我会将它们放入一个单独的ArrayList中,并在需要时逐一删除:

ArrayList<Integer> deadlines = new ArrayList<>();
for(Game game : inputData) {
if (!deadlines.contains(game.deadline))
deadlines.add(game.deadline);
}

之后的思想是一个接一个地迭代inputData,并跟踪previousDeadlinecurrentDeadline。如果我们认为currentDeadlinepreviousDeadline更伟大,那么这意味着我们不能接受在此之前的任何截止日期。这可以通过检查我们之前创建的deadlines列表并从中删除截止日期来完成

int previousDeadline = -1; // initial value because there is no -1 deadline
for(int i = inputData.size()-1; i >= 0; i--) {
int currentDeadline = inputData.get(i).deadline;

// compare previous and currentDeadline and remove from deadlines list
if(previousDeadline != -1 && previousDeadline < currentDeadline) {
for(int j = 1; j < currentDeadline; j++) {
deadlines.remove(Integer.valueOf(j));
}
}
// add to result only if present in deadlines list and update previous deadline
if (deadlines.contains(currentDeadline) ) {
result.add(inputData.get(i).number);
previousDeadline = inputData.get(i).deadline;
}
}

如果你给它一个你提供的输入,你会得到一个带有[3, 4, 1, 6]的列表

这是一个带有代码的粘贴箱

最新更新