首页 新闻 搜索 专区 学院

PTA 图 关键路径 测试点2和5

0
悬赏园豆:50 [已关闭问题] 关闭于 2020-04-10 13:00

关键活动 (30分)
假定一个工程项目由一组子任务构成,子任务之间有的可以并行执行,有的必须在完成了其它一些子任务后才能执行。“任务调度”包括一组子任务、以及每个子任务可以执行所依赖的子任务集。

比如完成一个专业的所有课程学习和毕业设计可以看成一个本科生要完成的一项工程,各门课程可以看成是子任务。有些课程可以同时开设,比如英语和C程序设计,它们没有必须先修哪门的约束;有些课程则不可以同时开设,因为它们有先后的依赖关系,比如C程序设计和数据结构两门课,必须先学习前者。

但是需要注意的是,对一组子任务,并不是任意的任务调度都是一个可行的方案。比如方案中存在“子任务A依赖于子任务B,子任务B依赖于子任务C,子任务C又依赖于子任务A”,那么这三个任务哪个都不能先执行,这就是一个不可行的方案。

任务调度问题中,如果还给出了完成每个子任务需要的时间,则我们可以算出完成整个工程需要的最短时间。在这些子任务中,有些任务即使推迟几天完成,也不会影响全局的工期;但是有些任务必须准时完成,否则整个项目的工期就要因此延误,这种任务就叫“关键活动”。

请编写程序判定一个给定的工程项目的任务调度是否可行;如果该调度方案可行,则计算完成整个工程项目需要的最短时间,并输出所有的关键活动。

输入格式:
输入第1行给出两个正整数N(≤100)和M,其中N是任务交接点(即衔接相互依赖的两个子任务的节点,例如:若任务2要在任务1完成后才开始,则两任务之间必有一个交接点)的数量。交接点按1N编号,M是子任务的数量,依次编号为1M。随后M行,每行给出了3个正整数,分别是该任务开始和完成涉及的交接点编号以及该任务所需的时间,整数间用空格分隔。

输出格式:
如果任务调度不可行,则输出0;否则第1行输出完成整个工程项目需要的时间,第2行开始输出所有关键活动,每个关键活动占一行,按格式“V->W”输出,其中V和W为该任务开始和完成涉及的交接点编号。关键活动输出的顺序规则是:任务开始的交接点编号小者优先,起点编号相同时,与输入时任务的顺序相反。

#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#define IMPOSSIBLE 1000000

typedef struct E * Edge;   //边
struct E{
    int e1, e2;
    int take;
};

typedef struct G * Graph;
struct G{
    int N;  //节点个数
    int E;  //边个数
    int ** Matrix;
};

typedef struct Q *Queue;    //队列
struct Q{
    int a[105];
    int front, rear;
};

Graph CreateGraph(int, int);
void InsertGraph(Graph, Edge);
int * GetEarilestCost(Graph, int *);    //得到每个节点了最早完成时间
int * GetLatestCost(Graph, int *, int *);
void PrintCriticalPath(Graph, int *, int *);

Queue CreateQueue();        //建队列
void InQueue(Queue , int);  //入队
int OutQueue(Queue);        //出队
bool IsEmpty(Queue);

int main()
{
    int N, M, i, ecost = 0;
    int *Earliest, *Latest;

    scanf("%d %d", &N, &M);
    Graph G = CreateGraph(N, M);

    int *Indegree = (int *)malloc( sizeof(int) * (N+1) );
    int *Outdegree = (int *)malloc( sizeof(int) * (N+1) );
    for( i=0; i<=N; ++i ){
        Indegree[i] = 0;
        Outdegree[i] = 0;
    }

    Edge E = (Edge)malloc(sizeof(struct E));
    for( i=1; i<=G->E; ++i ){
        scanf("%d %d %d", &E->e1, &E->e2, &E->take);
        InsertGraph(G, E);
        Indegree[E->e2]++;
        Outdegree[E->e1]++;
    }
    free(E);

    Earliest = GetEarilestCost(G, Indegree);
    if( !Earliest )  printf("0\n");    //有回路
    else{
        for( i=1; i<=G->N; ++i ){
            if( Earliest[i] > ecost )
                ecost = Earliest[i];
        }
        printf("%d\n", ecost);
        Latest = GetLatestCost(G, Outdegree, Earliest);
        /*for( i=1; i<=G->N; ++i ){
            printf("%d   %d\n", Earliest[i], Latest[i]);
        }*/

        PrintCriticalPath(G, Earliest, Latest);
    }

    return 0;
}

Graph CreateGraph(int N, int M)
{
    int i, j;

    Graph G = (Graph)malloc(sizeof(struct G));
    G->Matrix = (int **)malloc( sizeof(int *) * (N+1) );
    if( !G || !G->Matrix )   printf("Out of memory!");
    for( i=1; i<=N; ++i ){
        G->Matrix[i] = (int *)malloc( sizeof(int) * (N+1) );
        if( !G->Matrix[i] ) printf("Out of memory!");
        for( j=1; j<=N; ++j ){
            G->Matrix[i][j] = IMPOSSIBLE;
        }
    }
    G->N = N;
    G->E = M;

    return G;
}
void InsertGraph(Graph G, Edge E)
{
    G->Matrix[E->e1][E->e2] = E->take;
}
int * GetEarilestCost(Graph G, int *Indegree)
{
    int i, n, count = 0;  //n是弹出队列的节点 ,count用于计算节点个数,若循环结束时count不等于总节点个数,说明有回路
    Queue Q = CreateQueue();

    int *Cost = (int *)malloc( sizeof(int) * (G->N+1) );
    for( i=1; i<=G->N; ++i ){
        Cost[i] = 0;    //每个cost都初始化为0
        if( !Indegree[i] )  //找出初始入度为零节点,进入队列
            InQueue(Q, i);
    }

    while( !IsEmpty(Q) ){   //只要队列不空
        n = OutQueue(Q);
        count++;    //计数器
        for( i=1; i<=G->N; ++i ){
            if( G->Matrix[n][i] == IMPOSSIBLE )
                continue;
            if( !--Indegree[i] ){  //队列每弹出一个节点,他指向节点的入度减1,当入度为0时,入队列
                InQueue(Q, i);
            }
            if( Cost[i] < G->Matrix[n][i] + Cost[n])   //最早完成时间是所有到这个节点完成时间中最长的那个,
                Cost[i] = G->Matrix[n][i] + Cost[n];
        }
    }
    free(Q);
    if( count != G->N ) //说明有回路
        return NULL;
    else return Cost;
}
int * GetLatestCost(Graph G, int * Outdegree, int * Earliest)
{
    int i, n;
    Queue Q = CreateQueue();

    int *Cost = (int *)malloc( sizeof(int) * (G->N+1) );
    for( i=1; i<=G->N; ++i ){
        Cost[i] = IMPOSSIBLE;
        if( !Outdegree[i] ){  //找出初始入度为零节点,进入队列
            InQueue(Q, i);
            Cost[i] = Earliest[i];
        }
    }

    while( !IsEmpty(Q) ){   //只要队列不空
        n = OutQueue(Q);
        for( i=1; i<=G->N; ++i ){
            if( G->Matrix[i][n] == IMPOSSIBLE )
                continue;
            if( !--Outdegree[i] ){  //队列每弹出一个节点,他指向节点的入度减1,当入度为0时,入队列
                InQueue(Q, i);
            }
            if( Cost[i] > Cost[n] - G->Matrix[i][n] )   //最早完成时间是所有到这个节点完成时间中最长的那个,
                Cost[i] = Cost[n] - G->Matrix[i][n];
        }
    }
    free(Q);

    return Cost;
}
void PrintCriticalPath(Graph G, int *Earilest, int *Latest)
{
    int i, j;

    for( i=1; i<=G->N; ++i ){
        for( j=G->N; j>=1; --j ){
            if( G->Matrix[i][j] == IMPOSSIBLE )
                continue;
            if( G->Matrix[i][j] + Earilest[i] == Latest[j] )
                printf("%d->%d\n", i, j);
        }
    }
}

Queue CreateQueue()
{
    Queue Q = (Queue)malloc(sizeof(struct Q));
    Q->front = Q->rear = 0;
    return Q;
}
void InQueue(Queue Q, int i)
{
    Q->a[Q->rear] = i;
    Q->rear++;
}
int OutQueue(Queue Q)
{
    if( !IsEmpty(Q) ){
        int i = Q->a[Q->front];
        Q->front++;
        return i;
    }
    else printf("wrong\n");
}
bool IsEmpty(Queue Q)
{
    if( Q->front == Q->rear )
        return true;
    else
        return false;
}

测试点2和5一直过不去
我也看了很多人的答案,还是发现不了错误
求指导

り罐罐。的主页 り罐罐。 | 初学一级 | 园豆:46
提问于:2020-04-10 10:28
< >
分享
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册