基本概念
AOV网-用顶点表示活动的图,为DAG网
DAG网-有向无环图
拓扑排序的定义:在图论中,由一个有向无环图
的顶点组成的序列,当且仅当满足下列条
件时,称为该图的一个拓扑排序:
- 每个顶点出现且只出现一次。
- 若顶点A在序列中排在顶点B的前面,则在图中不存在从顶点到顶点A的路径。
每个AOV网都至少对应一个拓扑排序(不唯一)
拓扑排序的实现步骤:
- 从AOV网中选择一个没有前驱(入度为0)的J顶点并输出。
- 从网中删除该顶点和所有以它为起点的有向边。
- 重复1和2直到当前的AOV网为空或当前网中不存在无前驱的J顶点为止。
例题
7-33 任务调度的合理性
分数 25
作者 DS课程组
单位 浙江大学
假定一个工程项目由一组子任务构成,子任务之间有的可以并行执行,有的必须在完成了其它一些子任务后才能执行。“任务调度”包括一组子任务、以及每个子任务可以执行所依赖的子任务集。
比如完成一个专业的所有课程学习和毕业设计可以看成一个本科生要完成的一项工程,各门课程可以看成是子任务。有些课程可以同时开设,比如英语和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
#include<stdio.h>
#include<stdlib.h>
typedef struct Node {
int data;
int indegree = 0;
struct Node *next = NULL;
} Node;
Node work[100];
void putNode(Node *N, int add) {//动态链表存储邻接矩阵
Node *p = N;
p->next = (Node*)malloc(sizeof(Node));
p->next->data = add;
p = p->next; //指向尾端
p->next = NULL;
}
void deleteDegree(Node *N) {
Node *p = N;
while (p->next != NULL) {
p = p->next;
work[p->data].indegree--;
}
}
int main() {
int N, end = 0;
scanf("%d", &N);
//int print[100];
for (int i = 1; i <= N; i++) {//输入数据
int t;
scanf("%d", &t);
work[i].data = i;
Node *q = &work[i];
while (t--) {
int add;
scanf("%d", &add);
work[add].indegree++;
putNode(q, add);
q = q->next;
}
}
while (end != N) {
int flag = 0;
for (int i = 1; i <= N; i++) {
if (work[i].indegree == 0) {
//print[end++] = i;
end++;
deleteDegree(&work[i]);
work[i].indegree = -1;
flag = 1;
}
}
if (flag == 0 && end != N) {
printf("0");
return 0;
}
}
printf("1");
return 0;
}
优化代码:(参考王道)
#include<stdio.h>
#include<stdlib.h>
typedef struct Node {
int data;
int indegree = 0;
struct Node *next = NULL;
} Node;
Node work[100];//待排序的图
int print[100];//数组存储输出
int S[100];//伪队列
void putNode(Node *N, int add) {//动态链表存储邻接矩阵
Node *p = N;
p->next = (Node*)malloc(sizeof(Node));
p->next->data = add;
p = p->next; //指向尾端
p->next = NULL;
}
void TopologicalSort(int N) {
int top = 0, bottom = 0;
int count = 0;
for (int i = 1; i <= N; i++) {//将所有入度为0的点入栈
if (work[i].indegree == 0) {
S[top++] = i;
work[i].indegree = -1;
}
}
while (top != bottom) {//若栈非空
int t = S[bottom++];//弹出栈底
print[count++] = t;
Node *p = &work[t];
while (p->next != NULL) {//将相关联的边删除,关联点入度-1
p = p->next;
work[p->data].indegree--;
if (work[p->data].indegree == 0) {
S[top++] = p->data;
}
}
work[t].indegree = -1;
}
if (count == N)
printf("1");
else
printf("0");
}
int main() {
int N;
scanf("%d", &N);
for (int i = 1; i <= N; i++) {//输入数据
int t;
scanf("%d", &t);
work[i].data = i;
Node *q = &work[i];
while (t--) {
int add;
scanf("%d", &add);
work[add].indegree++;
putNode(q, add);//构造邻接表
q = q->next;
}
}
//拓扑排序
TopologicalSort(N);
return 0;
}