【数据结构与算法暑期实习】PTA 最短工期

一、题目

一个项目由若干个任务组成,任务之间有先后依赖顺序。项目经理需要设置一系列里程碑,在每个里程碑节点处检查任务的完成情况,并启动后续的任务。现给定一个项目中各个任务之间的关系,请你计算出这个项目的最早完工时间。

输入格式:
首先第一行给出两个正整数:项目里程碑的数量 N(≤100)和任务总数 M。这里的里程碑从 0 到 N−1 编号。随后 M 行,每行给出一项任务的描述,格式为“任务起始里程碑 任务结束里程碑 工作时长”,三个数字均为非负整数,以空格分隔。

输出格式:
如果整个项目的安排是合理可行的,在一行中输出最早完工时间;否则输出"Impossible"。

输入样例 1:
9 12
0 1 6
0 2 4
0 3 5
1 4 1
2 4 1
3 5 2
5 4 0
4 6 9
4 7 7
5 7 4
6 8 2
7 8 4
输出样例 1:
18
输入样例 2:
4 5
0 1 1
0 2 2
2 1 3
1 3 4
3 2 5
输出样例 2:
Impossible

二、思路

有向图在工业中的应用。考查AOV网络、拓扑排序以及关键路径问题。
项目安排是否合理可行:有向图是否存在回路(不存在则满足拓扑排序);
最早完工时间:即关键路径长度,从源点到汇点的最长的通路。

三、代码

数据结构使用的是图,存储方式为邻接矩阵(邻接表会更好)

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
using namespace std;

#define MaxVertexNum 200	/* 最大顶点数设为200 */
typedef int Vertex;         /* 用顶点下标表示顶点,为整型 */
typedef int WeightType;		/* 边的权值设为整型 */


/* 边定义 */
typedef struct ENode* PtrToENode;
struct ENode {
    Vertex v1, v2;      /* 有向边<V1, V2> */
    WeightType weight;  /* 权重 */
};
typedef PtrToENode Edge;


/* 图结点的定义 */
typedef struct GNode* PtrToGNode;
struct GNode {
    int Nv;  /* 顶点数 */
    int Ne;  /* 边数   */
    WeightType G[MaxVertexNum][MaxVertexNum]; /* 邻接矩阵 */
};
typedef PtrToGNode MGraph; /* 以邻接矩阵存储的图类型 */


/*函数声明*/
MGraph CreateGraph(int VertexNum);//初始化一个有VertexNum个顶点但没有边的图 
void InsertEdge(MGraph Graph, Edge E);// 插入边 <V1, V2> 
MGraph BuildGraph();//图的构建 
int max(int x, int y);
bool TopSort(MGraph Graph);//拓扑排序


/*主程序*/
int main() {
    MGraph graph = BuildGraph();
    TopSort(graph);

}


MGraph CreateGraph(int VertexNum)
{
    Vertex v, u;
    MGraph Graph;

    Graph = (MGraph)malloc(sizeof(struct GNode)); /* 建立图 */
    Graph->Nv = VertexNum;
    Graph->Ne = 0;
    /* 初始化邻接矩阵 */
    /* 注意:这里默认顶点编号从0开始,到(Graph->Nv - 1) */
    for (v = 0; v < Graph->Nv; v++) {
        for (u = 0; u < Graph->Nv; u++) {
            Graph->G[v][u] = 0;
        }
    }
    return Graph;
}



void InsertEdge(MGraph Graph, Edge E)
{
    Graph->G[E->v1][E->v2] = E->weight;
}


MGraph BuildGraph()
{
    MGraph Graph;
    Edge E;
    int Nv, i, v, u;

    scanf_s("%d", &Nv);   /* 读入顶点个数、边的个数 */
    Graph = CreateGraph(Nv); /* 初始化有Nv个顶点但没有边的图 */
    scanf_s("%d", &(Graph->Ne));   /* 读入边数 */

    for (v = 0; v < Graph->Nv; v++) {
        for (u = 0; u < Graph->Nv; u++) {
            Graph->G[v][u] = -1;
        }
    }

    if (Graph->Ne != 0) { /* 如果有边 */
        E = (Edge)malloc(sizeof(struct ENode)); /* 建立边结点 */
        /* 读入边,格式为"起点 终点 权重",插入邻接矩阵 */
        for (i = 0; i < Graph->Ne; i++) {
            scanf_s("%d %d %d", &E->v1, &E->v2, &E->weight);
            /* 注意:如果权重不是整型,Weight的读入格式要改 */
            InsertEdge(Graph, E);
        }
    }

    return Graph;
}




int max(int x, int y)
{
    if (x > y) return x;
    else return y;
}



bool TopSort(MGraph Graph) {
    int Indegree[MaxVertexNum];//记录每个顶点的入度
    int length[MaxVertexNum];
    memset(length, 0, sizeof(length));
    int v, u;
    int cnt = 0;
    int ans = 0;

    //先把每个顶点的入度都标记为0
    for (v = 0; v < Graph->Nv; v++) {
        Indegree[v] = 0;
    }

    //遍历所有的边,如果存在这条有向边,那么末结点入度加1.
    for (v = 0; v < Graph->Nv; v++) {
        for (u = 0; u < Graph->Nv; u++) {
            if (Graph->G[v][u] != -1) {
                Indegree[u]++;
            }
        }
    }

    while (1) {
        //如果一个图中,每个顶点的入度都大于0,则一定存在回路;如果不存在回路,就一定满足拓扑排序
        //拓扑排序:先找到一个入度为0的点,输出,从图中删去该顶点和与该顶点相邻的边;重复这一过程直到所有的点都被输出
        int flag = 0;
        for (v = 0; v < Graph->Nv; v++) {
            if (Indegree[v] == 0) {
                Indegree[v] = -1;
                flag = 1;
                cnt++;
                for (u = 0; u < Graph->Nv; u++) {
                    if (Graph->G[v][u] != -1) {
                        Indegree[u]--;
                        length[u] = max(length[u], length[v] + Graph->G[v][u]);
                        ans = max(ans, length[u]);
                    }
                }
            }
        }

        if (flag == 0) break;
    }

    if (cnt != Graph->Nv) printf("Impossible\n");
    else printf("%d\n", ans);
    return 0;

}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值