在铁人三项比赛中为团队实现最佳完成时间的算法[关闭]

Posted

技术标签:

【中文标题】在铁人三项比赛中为团队实现最佳完成时间的算法[关闭]【英文标题】:Algorithm for achieving best finish time for a team in a triathlon race [closed] 【发布时间】:2015-02-21 16:52:23 【问题描述】:

老师计划一场比赛,每位参赛者必须完成 20 圈的游泳,然后是 10 公里的自行车,然后是 3 公里的跑步。例如 换句话说,第一个参赛者游完 20 圈,下车,开始骑自行车。第一个人一离开游泳池,第二个参赛者就开始游 20 圈;当他或她出去并开始骑自行车时,第三名参赛者开始游泳。 . .等等

每个参赛者都有一个预计的游泳时间是指完成 20 圈游泳的预计时间,每个参赛者也有预计的骑行和跑步时间。问题是为比赛设计一个时间表,即参赛者开始的顺序。参与者可以同时骑自行车和比赛,但池中最多只有一个人。

问题:设计一种高效的算法来调度上述任务? 还有一个证明算法正确的程序。

尝试的解决方案: 我想出了这个主意: 将参赛者从1初始化到n, 设 si, bi, ri 表示游泳、骑自行车和跑步时间的选手 i 。 我们按照循环所用时间+跑步所用时间的递减顺序排列参赛者,并按此顺序发送。 我无法正确制定算法和程序,因此需要帮助。

【问题讨论】:

如果您想按所需时间对它们进行排序,请考虑使用 PriorityQueue,它可以让您以最快的时间获得下一个人。 您需要什么帮助?您能否发布到目前为止您尝试过的伪代码或实际的 Java 代码,以及您在哪里卡住了?否则,看起来您只是在要求人们为您完成这项任务。 【参考方案1】:

你只需要担心两个简单的规则:

    如果我们让花最少时间游泳的参赛者先游泳,我们将缩短参赛者之间的完成时间。 如果两名选手的游泳时间相同怎么办?在这种情况下,应先派出总跑步+骑行时间较多的参赛者,以便他们在剩余的活动中抢占先机。

考虑每位参赛者在特定活动上花费的时间:

Contestant->Swim time(mins)->Run time(mins)->Cycle time(mins)
A->1->1->1
B->2->1->1
C->1->1->1

案例 1: 游泳时间最长的选手先行。比赛从下午 12:00 开始,按照 B、A、C 的顺序,我们有:

Swimming complete->Running complete->Cycling complete
B->12:02->12:03->12:04
A->12:03->12:04->12:05
C->12:04->12:05->12:06

案例 2: 游泳时间最长的选手排名第二。比赛从下午 12:00 开始,按照 A、B、C 的顺序,我们有:

Swimming complete->Running complete->Cycling complete
A->12:01->12:02->12:03
B->12:03->12:04->12:05
C->12:04->12:05->12:06

案例 3: 游泳时间最长的选手获得第三名。比赛从下午 12:00 开始,我们有 A、C、B 的顺序:

Swimming complete->Running complete->Cycling complete
A->12:01->12:02->12:03
C->12:02->12:03->12:04
B->12:04->12:05->12:06

您可以看到,在所有情况下,最后一位参赛者在 12:06 完成比赛。但是,在案例 1(游泳时间最长的选手先行)中,第一位选手在下午 12:04 结束,第二位选手在 12:05 结束。情况二(游泳时间最长的选手第二),第一位选手在 12:03 完成比赛,第二位选手在 12:05 完成比赛。情况3(游泳时间最长的选手排在第三位),第一位选手在12:03结束比赛,第二位选手在12:04结束比赛。这是迄今为止最有效的顺序,因为到 12:04,您已经有两名选手完成了比赛。

但是,如果两个参赛者的游泳时间相同,但循环和跑步时间不同怎么办。考虑:

Contestant->Swim time(mins)->Run time(mins)->Cycle time(mins)
A->1->1->1
B->2->2->1
C->2->1->1

案例4:从两个游泳时间相同的选手中,骑行和跑步时间总和较低的选手先行:

Swimming complete->Running complete->Cycling complete
A->12:01->12:02->12:03
C->12:03->12:04->12:05
B->12:05->12:07->12:08

案例5:从两个游泳时间相同的选手中,自行车和跑步总时间多的选手先行:

Swimming complete->Running complete->Cycling complete
A->12:01->12:02->12:03
B->12:03->12:05->12:06
C->12:05->12:06->12:07

可以看出,在案例 4 中,最后一位参赛者在下午 12:08 完成比赛,而在案例 5 中,最后一位参赛者在下午 12:07 完成比赛。这意味着如果两个参赛者的游泳时间相同,则骑车+跑步总时间多的选手先走。

用 Java 编写代码:

首先创建一个类来保存参赛者信息:

public class Contestant 
    private String name;
    private Map<String,Integer> timings = new HashMap<>();

    public Contestant(String name, Map<String, Integer> timings) 
        this.name = name;
        this.timings = timings;
    

    public Integer getTimingFor(String activity) 
        return timings.get(activity);
    

    public Map<String, Integer> getTimings() 
        return timings;
    

    public String getName() 
        return name;
    



然后创建一个比较器来决定哪个参赛者应该先于另一名参赛者。思路是按照游泳时间的顺序(升序),然后按照剩余活动的顺序(降序)对参赛者进行排序

  public class ContestantComparator implements Comparator<Contestant> 

    @Override
    public int compare(Contestant one, Contestant two) 
        int contestantOneSwimTime = one.getTimingFor("Swimming");
        int contestantTwoSwimTime = two.getTimingFor("Swimming");

        if(contestantOneSwimTime<contestantTwoSwimTime) 
            return -1;
         else if(contestantOneSwimTime>contestantTwoSwimTime) 
            return 1;
         else 
            int c1RemainingTimeExceptSwimming = 0;
            int c2RemainingTimeExceptSwimming = 0;

            for(String activity : one.getTimings().keySet()) 
                if(!activity.equals("Swimming")) 
                    c1RemainingTimeExceptSwimming+=one.getTimingFor(activity);
                
            

            for(String activity : two.getTimings().keySet()) 
                if(!activity.equals("Swimming")) 
                    c2RemainingTimeExceptSwimming+=two.getTimingFor(activity);
                
            

            if(c1RemainingTimeExceptSwimming>c2RemainingTimeExceptSwimming) 
                return -1;
             else if(c1RemainingTimeExceptSwimming<c2RemainingTimeExceptSwimming) 
                return 1;
             else 
                return 0;
            
        
    

主类使用代码:

public class Contest 

    public static void main(String []args) 
        Map<String,Integer> timings = new HashMap<String,Integer>();
        timings.put("Swimming", 1);
        timings.put("Running", 1);
        timings.put("Cycling", 1);
        Contestant a = new Contestant("A",timings);

        timings = new HashMap<String,Integer>();
        timings.put("Swimming", 1);
        timings.put("Running", 2);
        timings.put("Cycling", 1);
        Contestant b = new Contestant("B",timings);

        timings = new HashMap<String,Integer>();
        timings.put("Swimming", 1);
        timings.put("Running", 2);
        timings.put("Cycling", 2);
        Contestant c = new Contestant("C",timings);

        List<Contestant> contestants = new ArrayList<Contestant>();
        contestants.add(a);
        contestants.add(b);
        contestants.add(c);
        Collections.sort(contestants,new ContestantComparator());

        for(Contestant contestant : contestants) 
            System.out.println(contestant.getName());
        

    


请注意,Contestant 类包含一个 Map。此地图的目的是允许您为参赛者添加任意数量的任务,而无需更改代码。键代表游泳等活动,值(整数)代表相应活动的时间。

【讨论】:

代码的时间复杂度是O(n^2)。 我是这么想的——如果有 n 位参赛者,那么我们将一直到最后 n 位来选择截止日期最早的人。这个循环将运行 n 次。我猜我的错在第一次去 T.C 将是 O(n) 哎呀,对不起,我不知道实际上我的笔记本电脑在这个网站上被冻结了,并且在当时随意点击它一定发生了,而且我是网站界面的新手,所以它需要是时候让我习惯了, 非常感谢您的时间和努力,虽然我对您的程序有一些疑问 我没有得到这部分代码的作用?否则 int P1TimeExceptSwimming = 0; int P2TimeExceptSwimming = 0; for(String activity : one.getTimings().keySet()) if(!activity.equals("Swimming")) P1TimeExceptSwimming+=one.getTimeFrame(activity); for(String activity : two.getTimings().keySet()) if(!activity.equals("Swimming")) P2TimeExceptSwimming+=two.getTimeFrame(activity); if(P1TimeExceptSwimming>P2TimeExceptSwimming) return -1;【参考方案2】:

这是一个贪婪编程的明显案例(注意:贪婪并非总是最佳选择): 我在这里假设您希望尽量减少总时间,因为这里没有提到。

算法:

    设 si、bi、ri 分别表示游泳、骑自行车和跑步时间的选手 i。

    按参赛者完成所有三项任务所需的时间排序,按降序排列。

    根据步骤 2 中的顺序安排它们

对参赛者进行排序:

    计算每个人游泳、骑车和跑步所需的时间。

    将这些时间相加,并按降序排列。

【讨论】:

这真的不像按总时间的降序排序那么简单。看我的回答。 它的贪婪方法,不确定它是否是最优的。但是我在这里做的是最慢的选手应该先送出去,以此类推,最快的选手应该最后送出去,因为他可以覆盖其他选手。在你的方法中,如果游泳时间最少的选手最后出局,但如果他的骑行和跑步时间非常非常大怎么办。你最终会增加总时间。所以在我看来,考虑到总时间会很好。 @bot 如果有 2 位参赛者 (10,10,10) 和 (9,20,30) 采用您的方法,您将发送第一个参赛者,因此当他在 10 点后完成游泳时,您将发送下一位参赛者。总时间:10 + 9 + 20 + 30 = 69 个单位,我会先发送参赛者 2:使用我的方法 总时间:9 + 20 + 30 = 59 个单位 这里有一个基本限制:The参与者可以同时骑自行车和比赛,但池中最多只有一个人。 我会先发送 9,20,30,然后再发送 10,10,10。看来你不明白我的做法。 @bot 好的,如果时间是 .. (9,10,10) & (10,20,30).. 使用您的方法将需要 9+ 10 + 20 + 30 = 69和我的方法需要10 + 20 + 30 = 60,顺便说一句,我不是来证明我的方法是对还是错,或者证明你的方法的任何事情。我来这里是为了学习自己和帮助他人,同时也了解解决问题的不同方法。所以你想要一个输入,例如我在这里给出的。

以上是关于在铁人三项比赛中为团队实现最佳完成时间的算法[关闭]的主要内容,如果未能解决你的问题,请参考以下文章

比赛调度算法起点

TencentOCR 斩获 ICDAR 2021 三项冠军

在遗传算法中为多个“推销员”TSP 实现交叉函数

WMT 2022国际机器翻译大赛发榜,微信翻译斩获三项任务冠军

14-个人课程总结

最佳分割算法[关闭]