本题与上一题类似,均是通过拓扑排序作为解题的关键步骤,该题唯一的不同就是需要计算权值来求得每个结点的最早发生时间。
点击这个链接可以快速查看拓扑排序的算法
7-2 最短工期
一个项目由若干个任务组成,任务之间有先后依赖顺序。项目经理需要设置一系列里程碑,在每个里程碑节点处检查任务的完成情况,并启动后续的任务。现给定一个项目中各个任务之间的关系,请你计算出这个项目的最早完工时间。
输入格式:
首先第一行给出两个正整数:项目里程碑的数量 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
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_VERTEX_NUM 100 // 最大顶点个数
#define OVERFLOW -1
#define FALSE 0;
#define TRUE 1
#define ERROR 0
#define OK 1
#define STACK_INIT_SIZE 100 //存储空间初始分配
#define STACKINCREMENT 10 //存储空间分配增量
typedef int Status;
typedef int VertexType;
// 顶点关系类型。对无权图,用1(是)或0(否)表示相邻否; 对带权图,则为权值类型
typedef int ArcCell,AdjMatrix[MAX_VERTEX_NUM][MAX_VERTEX_NUM];
typedef int SElemType;
typedef struct
{
SElemType *top;
SElemType *base;
int stacksize;
}SqStack;
Status InitStack(SqStack &s)
{s.base=(SElemType *)malloc(STACK_INIT_SIZE*sizeof(SElemType));
if (!s.base) exit(OVERFLOW); //存储分配失败
s.top=s.base;
s.stacksize = STACK_INIT_SIZE;
return OK;
}
Status GetTop(SqStack s, SElemType &e)
{ if(s.top==s.base) return ERROR;
e=*(s.top-1);
return OK;
}
Status Push(SqStack &s,SElemType e)
{//插入元素e为新的栈顶元素
if(s.top-s.base>=s.stacksize)
{//栈满,追加存储空间
s.base = (SElemType*)realloc(s.base, (s.stacksize+STACKINCREMENT)*sizeof(SElemType));
if(!s.base)exit(OVERFLOW); //存储分配失败
s.top = s.base + s.stacksize;
s.stacksize +=STACKINCREMENT;
}
*s.top++=e;
return OK;
}
Status Pop(SqStack &s,SElemType &e)
{ if(s.base==s.top) return ERROR;
e=*--s.top;
return OK;
}
Status StackEmpty(SqStack s)
{ if(s.base==s.top) return OK;
else return ERROR;
}
//图的邻接表存储表示
struct ArcNode
{
int info;
int adjvex; // 该弧所指向的顶点的位置
ArcNode *nextarc; // 指向下一条弧的指针
}; // 表结点
typedef struct
{
ArcNode *firstarc; // 第一个表结点的地址,指向第一条依附该顶点的弧的指针
}VNode,AdjList[MAX_VERTEX_NUM]; // 头结点
struct ALGraph
{
AdjList vertices;
int vexnum,arcnum; // 图的当前顶点数和弧数
int kind; // 图的种类标志
};
Status CreateGraph(ALGraph &G)
{ //采用头插法,构造有向网
int i,j,k;
int va,vb;
ArcNode *p;
scanf("%d %d",&G.vexnum,&G.arcnum);
for(int i=0;i<G.vexnum;i++){
G.vertices[i].firstarc=NULL;
}
for(k=0;k<G.arcnum;++k) // 构造表结点链表
{
int w;
scanf("%d %d %d",&va,&vb,&w);
i=va; // 弧尾
j=vb; // 弧头
p=(ArcNode*)malloc(sizeof(ArcNode));
p->adjvex=j;
p->info=w;
p->nextarc=G.vertices[i].firstarc; // 插在表头
G.vertices[i].firstarc=p;
}
return OK;
}
int FirstAdjVex(ALGraph G,VertexType v)
{ // 初始条件: 图G存在,v是G中某个顶点
// 操作结果: 返回v的第一个邻接顶点的序号。若顶点在G中没有邻接顶点,则返回-1
ArcNode *p;
int v1;
v1=v; // v1为顶点v在图G中的序号
p=G.vertices[v1].firstarc;
if(p)
return p->adjvex;
else
return -1;
}
void Display(ALGraph G)
{ // 输出图的邻接矩阵G
int i;
ArcNode *p;
printf("%d个顶点:\n",G.vexnum);
printf("\n%d条弧:\n",G.arcnum);
for(i=0;i<G.vexnum;i++)
{
p=G.vertices[i].firstarc;
while(p)
{
printf("%d→%d",i,p->adjvex);
p=p->nextarc;
printf("\n");
}
}
}
void FindInDegree(ALGraph G,int a[31]){
ArcNode *p;
for(int i=0;i<G.vexnum;i++){
p=G.vertices[i].firstarc;
while(p!=NULL){
a[p->adjvex]++;
p=p->nextarc;
}
}
}
int ve[102]={0};
Status TopologicalSort(ALGraph G){
int indegree[101]={0};
SqStack S,T;
FindInDegree(G,indegree);
InitStack(S);
InitStack(T);
int count = 0;
for(int i=0;i<G.vexnum;i++){
if(!indegree[i])Push(S,i); //入度为0的结点入栈
}
count = 0;
while(!StackEmpty(S)){
int e;
Pop(S,e);
Push(T,e);
count ++;
for(ArcNode* p=G.vertices[e].firstarc;p;p=p->nextarc){
int k=p->adjvex;
if(!(--indegree[k]))Push(S,k); //对j号顶点的每个邻接点入度减1
if(ve[e]+(p->info)>ve[k])
ve[k]=ve[e]+(p->info);
//计算最早发生时间,等于j号顶点的每个邻接点权值的最大值加上该顶点的最早发生时间
}
}
if(count<G.vexnum)return ERROR;
else
return OK;
}
int main(){
ALGraph G;
CreateGraph(G);
if(!TopologicalSort(G)){ //拓扑排序失败
printf("Impossible");
}else{
int max=0,j=0;
for(int i=0;i<G.vexnum;i++){
//由于工程需要全部结点均做完,所以求最早发生时间最大值,即为最短工期
if(max<ve[i])
{
max=ve[i];
j=i;
}
}
printf("%d",max);
}
}