7-34 任务调度的合理性(拓扑排序)

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

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

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

输入格式:

输入说明:输入第一行给出子任务数N(≤100),子任务按1~N编号。随后N行,每行给出一个子任务的依赖集合:首先给出依赖集合中的子任务数K,随后给出K个子任务编号,整数之间都用空格分隔。

输出格式:

如果方案可行,则输出1,否则输出0。

输入样例1:

12
0
0
2 1 2
0
1 4
1 5
2 3 6
1 3
2 7 8
1 7
1 10
1 7

输出样例1:

1

输入样例2:

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

输出样例2:

0

  

分析:

       首先看百度百科上关于拓扑排序的定义:对一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边(u,v)∈E(G),则u在线性序列中出现在v之前。通常,这样的线性序列称为满足拓扑次序(Topological Order)的序列,简称拓扑序列。简单的说,由某个集合上的一个偏序得到该集合上的一个全序,这个操作称之为拓扑排序。

       拓扑序列中除了顶点的所有点都有前驱,即入度不为0。那么如果我们想要找顶点也很简单,只需要在一开始选择入度为零的点即可。将该点送入队列,删除该点及相关的边,更新入度数组,找出新的度为零的点(可能是多个,拓扑序列因此也不唯一)。

       简单说来,拓扑排序算法(之一)的步骤为:

       ①找出入度为0的点,并进入队列

       ②当队列不为空,从队首取得一个元素a,cnt(记录个数)加1,与a相连的点入度减一,如果更新的入度为0,则进入队列。

 

       为什么这么做就可以确定一个DAG(Directed Acyclic Graph)中是否有拓扑序列呢?个人是这样理解的,一开始没有前驱的点必定是顶点,否则其他点是无法到达的。将与该点相关的边删除后,选择度为0的点,这些点除了当前点是没有其他路径可以到达的。那么选择入度为0的点,选遍所有点,即可得到一个拓扑序列。现在只有一个问题,就是删除队列中现有点及其边之后,每次更新入度数组后是否能够保证总会出现度为0的点?

       为了解决这个问题,我们可以采用假设法。假设剩余的n个点入度都大于等于1,你会发现这个子图就不再是有向无环图了。我们开始的图是DAG,那么它的子图也是DAG,既然有向无环,那么必定有入度为0的点。

       代码中TopNum数组用来记录选择出的拓扑序列,虽然对于本题是无用的……但我还是写了上去。如下:

#include<iostream>

using namespace std;

void Topsort(int graph[][101], int NumVertex);

int main(){
	int graph[101][101];
	int N, task, pretask;
	for(int i = 0; i < 101; i++)
		for(int j = 0; j < 101; j++)
			graph[i][j] = 0;
	cin >> N;
	for(int i = 0; i < N; i++){
		cin >> task;
		if(task != 0){
			for(int j = 0; j < task; j++){
				cin >> pretask;
				graph[pretask][i + 1] = 1;
			}
		}
	}
	Topsort(graph, N);	
}

void Topsort(int graph[][101], int NumVertex){
	int TopNum[101], Indegree[101];
	int queue[101], head = 0, tail = 0;
	int cnt = 0, V, W;
	//Create queue and make it empty
	for(int i = 0; i < 101; i++)
		queue[i] = -1;
	//Initial Indegree array
	for(int i = 1; i <= NumVertex; i++)
		Indegree[i] = 0;
	//find vertexes whose indegree is zero and put vertexes into the queue 
	//Set the Indegree array
	for(int i = 1; i <= NumVertex; i++){
		int flag = 1, j;
		for(j = 1; j <= NumVertex; j++){
			if(graph[j][i] == 1){
				flag = 0;
				Indegree[i]++;
			}
		}
		if(flag){
			queue[++tail] = i;
		}
	}
	//when queue is not empty
	while(head != tail){
		V = queue[++head];						
		TopNum[V] = ++cnt;
		for(W = 1; W <= NumVertex; W++){
			if(graph[V][W] == 1){
				if(--Indegree[W] == 0)
					queue[++tail] = W;
			}
		}
	}
	if(cnt != NumVertex)
		cout << '0';
	else
		cout << '1';
}

 

    原文作者:拓扑排序
    原文地址: https://blog.csdn.net/LightInDarkness/article/details/82503801
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞