C语言实现教学计划编制问题(图的拓扑排序)

[问题描述]
大学的每个专业都要制定教学计划。假设任何专业都有固定的学习年限,每学年含两学期,每学期的时间长度和学分上限值均相等,每个专业开设的课程都是确定的,而且课程在开设时间的安排必须满足先修关系。每门课程有哪些先修课程是确定的,可以有任意多门,也可以没有。每门课恰好占一个学期。试在这样的前提下设计一个教学计划编制程序。
[基本要求]
(1)输入参数包括:学期总数,一学期的学分上限,每门课的课程号(固定占3位的字母数字串)、学分和直接先修课的课程号。
(2)允许用户指定下列两种编排策略之一:一是使学生在各学期中的学习负担尽量均匀;二是使课程尽可能地集中在前几个学期中。
(3)若根据给定的条件问题无解,则报告适当的信息;否则将教学计划输出到用户指定的文件中。计划的表格格式自行设计。
[测试数据]
学期总数:6;学分上限:10;该专业共开设12门课,课程号从C01到C12,学分顺序为2,3,4,3,2,3,4,4,7,5,2,3。先修关系如下:
课程编号 课程名称 先决条件
C1 程序设计基础 无
C2 离散数学 C1
C3 数据结构 C1,C2
C4 汇编语言 C1
C5 语言的设计和分析 C3,C4
C6 计算机原理 C11
C7 编译原理 C5,C3
C8 操作系统 C3,C6
C9 高等数学 无
C10 线性代数 C9
C11 普通物理 C9
C12 数值分析 C9,C10,C1
[实现提示]
可设学期总数不超过12,课程总数不超过100。如果输入的先修课程号不在该专业开设的课程序列中,则作为错误处理。应建立内部课程序号与课程号之间的对应关系。

为了调试方便,从文件中读取信息,其中文件地址在宏定义,文件内容如图(切记最后一行要有回车!)
C语言实现教学计划编制问题(图的拓扑排序)_第1张图片
注意图中光标位置(最后一行要有回车)
文件内容如下:

6 10 12
C1 2
C2 3 C1
C3 4 C1 C2
C4 3 C1
C5 2 C3 C4
C6 3 C11
C7 4 C5 C3
C8 4 C3 C6
C9 7
C10 5 C9
C11 2 C9
C12 3 C9 C10 C1

运行情况:
C语言实现教学计划编制问题(图的拓扑排序)_第2张图片
C语言实现教学计划编制问题(图的拓扑排序)_第3张图片

代码如下:

/*
	实现内容:
	教学计划编制问题(图的拓扑排序)

	VS2019 编译通过
	2020.8.2 王大花
*/

#define _CRT_SECURE_NO_WARNINGS
#include 
#include 
#include 
#include 

#define VexDateType char
#define StackElemType int
#define MaxVexDateLength 3
#define MaxClassNum 100
#define MaxSemesterNum 12
#define TextPath "C:\\Users\\86132\\Desktop\\拓扑排序\\拓扑排序\\ALGraph.txt"
#define TeachPlanPath "C:\\Users\\86132\\Desktop\\拓扑排序\\拓扑排序\\TeachingPlan.txt"

//邻接表表示法

//弧结点
typedef struct _ARCNODE ArcNode;
struct _ARCNODE {
	int AdjVex;
	ArcNode* Next;
};

//顶点结点
typedef struct _VEXNODE {
	VexDateType Date[MaxVexDateLength + 1];
	int Credit;
	ArcNode* FirstArc;

}VexNode;

//学期信息
typedef struct _INFO {
	int SemesterNum;
	int MaxCredit;

}Info;

//图
typedef struct _ALGRAPH {
	VexNode* Vertics;
	int VexNum;
	int ArcNum;
	int* InDegree;
	Info* ExtraInfo;

}ALGraph;

//栈辅助拓扑排序(pos便于后续输出课程)
typedef struct _STACK {
	StackElemType* Vertics;
	int tail, pos;

}Stack;

void InitStack(Stack* S) {
	S->Vertics = (StackElemType*)malloc(MaxClassNum * sizeof(StackElemType));
	S->pos = S->tail = 0;

}


//根据元素寻找顶点位置 默认元素为C1C2C3……顺序排布
int Locate(char* ch) {
	return (2 == strlen(ch)) ? ch[1] - '1' : (ch[1] - '0') * 10 + ch[2] - '1';

}

//从文件读取信息
void Creat_ALGraph(ALGraph* G) {

	//初始化指针
	G->ExtraInfo = (Info*)malloc(sizeof(Info));

	//从文件读取信息
	FILE* fp = fopen(TextPath, "r");
	if (NULL == fp) {
		printf("文件路径有误");
		exit(EXIT_FAILURE);
	}

	//读学期总数 学分上限 课程总数(顶点数量)
	fscanf(fp, "%d%d%d", &G->ExtraInfo->SemesterNum, &G->ExtraInfo->MaxCredit, &G->VexNum);
	G->ArcNum = 0;

	if (G->VexNum > MaxClassNum) {
		printf("超出最大课程数%d,请更改数据\n", MaxClassNum);
		exit(EXIT_FAILURE);
	}
	if (G->ExtraInfo->SemesterNum > 12) {
		printf("超出最大学期数%d,请更改数据\n", MaxSemesterNum);
		exit(EXIT_FAILURE);
	}
	G->Vertics = (VexNode*)malloc(sizeof(VexNode) * G->VexNum);

	for (int i = 0; i < G->VexNum; i++)
		G->Vertics[i].FirstArc = NULL;

	//读取课程信息
	for (int i = 0; i < G->VexNum; i++) {

		//读取课程名称和学分
		fscanf(fp, "%s%d", G->Vertics[i].Date, &G->Vertics[i].Credit);

		//根据前序课程建立弧节点
		while ('\n' != fgetc(fp)) {
			char str[4];
			int pos;

			fscanf(fp, "%s", str);
			pos = Locate(str);

			//判断课程是不是没有
			if (pos < 0 || pos > G->VexNum) {
				printf("%s请输入正确的先决条件!\n", G->Vertics[i].Date);
				exit(EXIT_FAILURE);
			}


			//更新弧节点 注意是pos指向i的弧
			ArcNode* p = (ArcNode*)malloc(sizeof(ArcNode));
			p->AdjVex = i;
			p->Next = G->Vertics[pos].FirstArc;
			G->Vertics[pos].FirstArc = p;
			G->ArcNum++;
		}
	}
	fclose(fp);

	//更新入度数组
	G->InDegree = (int*)malloc(sizeof(int) * G->VexNum);
	memset(G->InDegree, 0, sizeof(int) * G->VexNum);

	for (int i = 0; i < G->VexNum; i++) {
		for (ArcNode* p = G->Vertics[i].FirstArc; NULL != p; p = p->Next) {
			G->InDegree[p->AdjVex]++;
		}
	}
}

//各学期负担集中
void Method1(ALGraph G, Stack S) {

	int TotalCreadit = 0;
	for (int i = 0; i < G.VexNum; i++)
		TotalCreadit += G.Vertics[i].Credit;
	int EachSemCredit = TotalCreadit / G.ExtraInfo->SemesterNum;
	FILE* fp = fopen(TeachPlanPath, "w");


	for (int i = 0; i < G.ExtraInfo->SemesterNum; i++) {
		int tmp = 0;
		printf("\n第%d个学期的课程:", i + 1);
		fprintf(fp, "\n第%d个学期的课程:", i + 1);

		do {
			printf("%s ", G.Vertics[S.Vertics[S.pos]].Date);
			fprintf(fp, "%s ", G.Vertics[S.Vertics[S.pos]].Date);
			tmp += G.Vertics[S.Vertics[S.pos++]].Credit;
		} while (tmp < EachSemCredit && S.pos < G.VexNum);
	}
	fclose(fp);
}

//集中在前几个学期
void Method2(ALGraph G) {

	FILE* fp = fopen(TeachPlanPath, "w");

	//重新更新入度数组
	memset(G.InDegree, 0, sizeof(int) * G.VexNum);

	for (int i = 0; i < G.VexNum; i++) {
		for (ArcNode* p = G.Vertics[i].FirstArc; NULL != p; p = p->Next) {
			G.InDegree[p->AdjVex]++;
		}
	}
	int count = 0;

	//记忆哪些课程是上学期学的
	int* Mark = (int*)malloc(sizeof(int) * G.VexNum);

	//外层循环为学期数
	for (int i = 0; i < G.ExtraInfo->SemesterNum; i++) {
		printf("\n第%d个学期的课程:", i + 1);
		fprintf(fp, "\n第%d个学期的课程:", i + 1);
		memset(Mark, 0, sizeof(int) * G.VexNum);

		//扫描一遍入度结点把所有为0的输出
		for (int j = 0; j < G.VexNum; j++) {
			if (0 == G.InDegree[j]) {
				count++;
				Mark[j] = 1;
				G.InDegree[j]--;
				printf("%s ", G.Vertics[j].Date);
				fprintf(fp, "%s ", G.Vertics[j].Date);
			}
		}
		if (G.VexNum == count)
			break;
		//扫描Mark数组 通过标记为1的位置来更新入度结点
		for (int j = 0; j < G.VexNum; j++) {
			if (Mark[j]) {

				for (ArcNode* p = G.Vertics[j].FirstArc; NULL != p; p = p->Next)
					G.InDegree[p->AdjVex]--;
			}
		}
	}
	fclose(fp);
}

void TopologicalSort(ALGraph G) {

	Stack S, OutCome;
	InitStack(&S);
	InitStack(&OutCome);

	//寻找入读为0的顶点入栈
	for (int i = 0; i < G.VexNum; i++) {
		if (!G.InDegree[i]) {
			S.Vertics[S.tail++] = i;
		}
	}

	while (0 < S.tail) {

		int tmp = OutCome.Vertics[OutCome.tail++] = S.Vertics[--S.tail];
		for (ArcNode* p = G.Vertics[tmp].FirstArc; NULL != p; p = p->Next) {
			G.InDegree[p->AdjVex]--;
			if (0 == G.InDegree[p->AdjVex])
				S.Vertics[S.tail++] = p->AdjVex;
		}
	}

	if (OutCome.tail != G.VexNum) {
		printf("课程的先决条件有误!\n");
		exit(EXIT_FAILURE);
	}
	printf("请输入分配课程要求(各学期负担集中:1 尽可能集中前几个学期:2):");

	int Command;
	scanf("%d", &Command);

	switch (Command) {
	case 1: Method1(G, OutCome); break;
	case 2: Method2(G); break;
	default: {
		printf("命令错误!");
		exit(EXIT_FAILURE);
	}
	}
}

int main(void) {

	ALGraph G;
	Creat_ALGraph(&G);
	TopologicalSort(G);

	return 0;
}

你可能感兴趣的:(数据结构学习记录,算法,数据结构,c语言)