08-图8 How Long Does It Take(拓扑排序)

该博客讨论了如何在给定项目活动的关系中找到最早的完成时间。它介绍了输入和输出规格,并提供了两个示例输入和输出。解题策略涉及拓扑排序,通过检查有向无环图(DAG)的存在来避免环路。拓扑排序的代码实现也被给出,包括判断图是否为DAG的方法。最后,博客给出了一个C++代码示例,用于计算项目的最早完成时间。如果无法调度,输出'Impossible'。
摘要由CSDN通过智能技术生成

Given the relations of all the activities of a project, you are supposed to find the earliest completion time of the project.

Input Specification:

Each input file contains one test case. Each case starts with a line containing two positive integers N (≤100), the number of activity check points (hence it is assumed that the check points are numbered from 0 to N−1), and M, the number of activities. Then M lines follow, each gives the description of an activity. For the i-th activity, three non-negative numbers are given: S[i]E[i], and L[i], where S[i] is the index of the starting check point, E[i] of the ending check point, and L[i] the lasting time of the activity. The numbers in a line are separated by a space.

Output Specification:

For each test case, if the scheduling is possible, print in a line its earliest completion time; or simply output "Impossible".

Sample Input 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

Sample Output 1:

18

Sample Input 2:

4 5
0 1 1
0 2 2
2 1 3
1 3 4
3 2 5

Sample Output 2:

Impossible

      题目基本大意

        给定一个AOE图,要求输出该项目的最早完成时间,否则输出“Impossible”(即当该图有环时输出“Impossible”)。

        解题思路:

        解决该问题就是要利用拓扑排序(加一个记录每个事件的最早完成时间的数组)即可得到每个事件(即顶点)的最早完成时间,而题目要求输出的最大的事件的最早完成时间(即终点)(注意注意,终点并不是序号最大的那个顶点哦,而是最早完成时间最大的那个点哦),而检查一个该图是否为有向无环图(DAG)只需要加一个计数变量就可以啦~

        拓扑排序:

拓扑序:如果v到w有一条有向边,那v一定排在w的前面,这样的序列成为拓扑序。)

   得到拓扑序的过程就成为拓扑排序。如果能够得到一个合理的拓扑序列,那么可以判断该图为有向无环图(DAG)

拓扑排序代码

/* 邻接表存储 - 拓扑排序算法 */

bool TopSort( LGraph Graph, Vertex TopOrder[] )
{ /* 对Graph进行拓扑排序,  TopOrder[]顺序存储排序后的顶点下标 */
    int Indegree[MaxVertexNum], cnt;
    Vertex V;
    PtrToAdjVNode W;
       Queue Q = CreateQueue( Graph->Nv );
 
    /* 初始化Indegree[] */
    for (V=0; V<Graph->Nv; V++)
        Indegree[V] = 0;
        
    /* 遍历图,得到Indegree[] */
    for (V=0; V<Graph->Nv; V++)
        for (W=Graph->G[V].FirstEdge; W; W=W->Next)
            Indegree[W->AdjV]++; /* 对有向边<V, W->AdjV>累计终点的入度 */
            
    /* 将所有入度为0的顶点入列 */
    for (V=0; V<Graph->Nv; V++)
        if ( Indegree[V]==0 )
            AddQ(Q, V);
            
    /* 下面进入拓扑排序 */ 
    cnt = 0; 
    while( !IsEmpty(Q) ){
        V = DeleteQ(Q); /* 弹出一个入度为0的顶点 */
        TopOrder[cnt++] = V; /* 将之存为结果序列的下一个元素 */
        /* 对V的每个邻接点W->AdjV */
        for ( W=Graph->G[V].FirstEdge; W; W=W->Next )
            if ( --Indegree[W->AdjV] == 0 )/* 若删除V使得W->AdjV入度为0 */
                AddQ(Q, W->AdjV); /* 则该顶点入列 */ 
    } /* while结束*/
    
    if ( cnt != Graph->Nv )
        return false; /* 说明图中有回路, 返回不成功标志 */ 
    else
        return true;
}

    关于如何判断一个图是不是DAG,看:从任一顶点开始遍历,会不会重新遍历到该顶点。

具体方法有两种:

        一、BFS。(拓扑排序即是BFS的变形)

        二、DFS。

代码(拓扑排序):


#include<stdio.h>
#define MaxSize 101
#define INF 1000
#include<queue>
using namespace std;
int N,M,etime[MaxSize];//存储最早完成时间
int G[MaxSize][MaxSize];//有向图 
int Indegree[MaxSize];//装各个顶点的入度 
queue<int>Q;
void CreateGraph(){
	//建图 
	int i,j,k;
	scanf("%d%d",&N,&M);
	for(i=0;i<N;i++){
		for(j=0;j<N;j++){
			if(i==j)G[i][j]=0;
			else G[i][j]=INF;
		}
	}
	for(k=0;k<M;k++){
		scanf("%d%d",&i,&j);
		scanf("%d",&G[i][j]);
	}
	while(!Q.empty())Q.pop();
}
bool TopSort(){
	//拓扑排序 
	int i,j,v,w;
	int count=0;
	for(i=0;i<N;i++){
		Indegree[i]=etime[i]=0;
		for(j=0;j<N;j++){
			if(i!=j&&G[j][i]!=INF) {
				Indegree[i]++;
			}
		}
		if(Indegree[i]==0){
			Q.push(i);
			etime[i]=0;	
		}
	}
	while(!Q.empty()){
		v=Q.front();
		Q.pop();
		count++;
		for(i=0;i<N;i++){
			if(v!=i&&G[v][i]!=INF){
				Indegree[i]--;
				if(Indegree[i]==0)Q.push(i);
				if(etime[v]+G[v][i]>etime[i])etime[i]=etime[v]+G[v][i];
			}
		}
	}
	if(count!=N)return false;
	return true;
}
int MaxEtime(){
	//找出该图的终点,即etime[]值最大的那个点 
	int i,Maxnum=etime[N-1];
	for(i=0;i<N-1;i++){
		if(etime[i]>Maxnum) Maxnum=etime[i];
	}
	return Maxnum;
} 
int main(){ 
	CreateGraph();
	if(!TopSort())printf("Impossible");
	else 
		printf("%d",MaxEtime());//终点不一定是顶点编号最大的那个点哦 
	return 0;
} 

提交结果:

希望对你有所帮助鸭~ 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值