专栏名称: 36大数据
关注大数据和互联网趋势,最大,最权威,最干货的大数据微信号(dashuju36)。大数据第一科技媒体。不发软文,只做知识分享。
目录
相关文章推荐
网信内蒙古  ·  解读2024年全国数据资源调查情况 ·  16 小时前  
网信内蒙古  ·  解读2024年全国数据资源调查情况 ·  16 小时前  
InfoTech  ·  DeepSeek更新了! ·  4 天前  
人工智能与大数据技术  ·  AI编程新王Claude ... ·  4 天前  
人工智能与大数据技术  ·  15亿美元AI独角兽崩塌,全是印度程序员冒充 ... ·  3 天前  
51好读  ›  专栏  ›  36大数据

数据结构与算法–关键路径

36大数据  · 公众号  · 大数据  · 2017-09-30 07:50

正文

请到「今天看啥」查看全文


各条从s到t的路径中(想象成各条生产线),找出最长的那条(费时最长的那条生产线),这条0 -> 9 -> 6 -> 8 -> 2就是关键路径,按照这个顺序执行任务就能使得完成整个工程总时间最短。


我们用代码测试一下。

package Chap7;

public class CPM {
    private AcycliLP lp;
    private int s; // 虚拟的起点
    private int t; // 虚拟的终点
    private int jobsNum; // 任务个数

    public CPM(double[] jobDuration, int[][] mustCompletedBefore) {
        jobsNum = jobDuration.length;
        // 设置两个虚拟顶点,代表起点和终点
        EdgeWeightedDiGraph> graph = new EdgeWeightedDiGraph<>(jobsNum + 2);

        s = jobDuration.length; // 起点
        t = s + 1; // 终点

        for (int i = 0; i < jobsNum; i++) {
            // 每个顶点都可能成为最先开工的,所以虚拟起点指向所有顶点,且费时都为0
            graph.addDiEdge(new DiEdge(s, i, 0.0));
            // 每个顶点都可能成为工程收尾的活动,所有顶点都指向该虚拟终点,费时自然是每个活动所持续的时间
            graph.addDiEdge(new DiEdge(i, t, jobDuration[i]));
            // 任务i必须在任务j之前完成, 即加入i -> j的有向边
            for (int j = 0; j < mustCompletedBefore[i].length; j++) {
                int successor = mustCompletedBefore[i][j];
                graph.addDiEdge(new DiEdge(i, successor, jobDuration[i]));
            }
            // 找到到每个活动的最长路径
            lp = new AcycliLP(graph, s);
        }

    }

    public void printJobExecuteOrder() {
        System.out.println("各任务开始时间表:");
        for (int i = 0; i < jobsNum; i++) {
            System.out.println(i + ": " + lp.distTo(i));
        }
        System.out.println("\n按照以下顺序执行任务,开始时间相同的任务同时执行。");
        for (DiEdge edge : lp.pathTo(t)) {
            // 遇到起点不打印箭头
            if (edge.from() == s) {
                System.out.print(edge.to());
            }
            // 最后一个任务在前一个顶点的就打印过了,遇到最后一条边换行就行
            else if (edge.to() == t) {
                System.out.println();
            } else {
                System.out.print(" -> " + edge.to());
            }
        }

        System.out.println("总共需要" + lp.distTo(t));
    }

    public static void main(String[] args) {
        // 每个任务的持续时间
        double[] duration = {41.0, 51.0, 50.0, 36.0, 38.0, 45.0, 21.0, 32.0, 32.0, 29.0};
        int[][] before = {{1, 7, 9}, {2}, {}, {}, {}, {}, {3, 8}, {3, 8}, {2}, {4, 6}};
        CPM cpm = new CPM(duration, before);
        cpm.printJobExecuteOrder();
    }

}

根据任务表,给图增加边。

  • 虚拟起点到所有顶点的边,且权值为0;

  • 所有顶点到虚拟终点的边,且权值为顶点任务持续时间。

  • 某任务v必须在一些任务之前完成的边,且权值为任务v的持续时长。比如0必须在1、7、9之前,则增加0 -> 1,0 -> 7,0 -> 9边,且权值都为任务0的持续时长。


接下来通过AcycliLP类求得关键路径,distTo[i]就表示i任务的开始时间,distTo[t]表示做完整个工程需要的最短时间;pathTo[i]表示执行到任务i的关键路径,自然pathTo[t]就是整个工程的关键路径。


上面的代码会打印如下结果:

各任务开始时间表:
0: 0.0
1: 41.0
2: 123.0
3: 91.0
4: 70.0
5: 0.0
6: 70.0
7: 41.0
8: 91.0
9: 41.0

按照以下顺序执行任务,开始时间相同的任务同时执行。
0 -> 9 -> 6 -> 8 -> 2
总共需要173.0

可以清楚地看到每个任务应该在什么时刻开始动工,只要按照0 -> 9 -> 6 -> 8 -> 2这样顺序执行就好。而且,有些任务开始时间一样,表示我们应该同时执行它们以节省时间。所以最后的方案是:0、5最先并同时执行,只要0做完了,就开始同时执行1、7、9,只要9做完了立刻同时执行4、6,一旦6做完了同时执行3、8,8只要完工紧接着做2,最后等待2也终于做完,整个工程竣工!需要的最短时间为173.0。


关键路径中的任务都是做完后就立刻执行下一个的(比如0执行完立刻执行9,9完工立刻执行6…),不用等待和它一起开工的其他任务也做完,因为这些任务在花费最长时间的这条关键路径上,它们迟早都能做完的。因此关键路径中,各个关键任务是不会有空闲和等待时间。如下图是上面任务表的执行流程

0 -> 9 -> 6 -> 8 -> 2一气呵成,中间毫无停顿。而且其他任务在这条生产线执行过程中均已完成!

End

阅读排行榜/精华推荐
1






请到「今天看啥」查看全文