教学计划编制问题试验5.docx
《教学计划编制问题试验5.docx》由会员分享,可在线阅读,更多相关《教学计划编制问题试验5.docx(16页珍藏版)》请在冰豆网上搜索。
教学计划编制问题试验5
HUNANUNIVERSITY
实验五最终报告
目:
教学计划编制问题题
姓名生学号生学学班级专业师导指老15月日52014年成完期日
一、需求分析
1.输入形式:
用户通过键盘输入课程总数、每门课的课程编号(固定占3位的字母数字串)和直接先修的课程号等的参数。
不对非法输入做处理,假定输入的数据都合法。
2.输出形式:
如果拓扑排序成功,输出拓扑排序后的教学计划编制的顺序;
如果拓扑排序不成功,输出排序错误信息,结束程序。
3.程序功能:
对于用户输入的一组课程编号,根据输入的先修顺序创建邻接矩阵进行存储,并输出拓扑排序后的课程编号的顺序。
4.测试数据
输入:
输入课程总数:
3
输入每门课的课程编号:
A01
是否有直接先修的课程(T/F):
F
输入每门课的课程编号:
A02
是否有直接先修的课程(T/F):
T
先修课程编号:
A01
是否有直接先修的课程(T/F):
F
输入每门课的课程编号:
A03
是否有直接先修的课程(T/F):
T
先修课程编号:
A02
是否有直接先修的课程(T/F):
F
输出:
教学计划编制完成,课程修读顺序为:
A01,A02,A03
(输入有误)课程输入错误!
教学计划编制失败,请重新输入。
二、概要设计
抽象数据类型
题设要求使用一个有向图表示教学计划,顶点表示某门课程,有向边表示课程之间的先修关系,数据的对象是图中的每一个顶点和有向边。
由此为本问题确定一个图的数据关系。
拓扑排序可以用顶点入度为0的方法实现,所以为实现拓扑排序的顶点顺序的存放,创建一个队列来存放。
图的ADT
数据对象:
V,R(分别代表某门课程的顶点组成的一个顶点集V和代表课程先修关系的有向弧边组成的一个弧集R。
)
数据关系:
VR={|v,w∈V且P(v,w)}
表示从v到w的一条弧,并称v为弧头,w为弧尾。
基本操作:
intn();//返回图中的顶点数
intfirst(int);//返回该点的第一条邻边
intnext(int);//返回该店的下一条邻边
voidsetEdge(int,int,int);//为有向边设置权值
intgetMark(int);//获得顶点的标志值
为顶点设置标志值//voidsetMark(int);
ADT
队列int
数据对象:
:
数据关系.n}…,a∈car,i=1,2,3R={|aii-1i-1i为队列尾。
a为队列头,an约定1
基本操作:
队列结构初始化//queue();
结构销毁操作//~queue();
数据入列//boolpush(constint&it);
数据出列//boolpop(int&it);
intsize();//获取队列长度算法的基本思想课程编号作为)初始化一个表示有向图的相邻矩阵,通过用户输入的顶点的个数(课程数为了检验图中)完成一个有向图的构建。
相邻矩阵的行列值以及有向边的关系(课程先修关系,当一个顶点经过拓扑排序后更顶点是否都经过拓扑排序,为每个顶点初始化一个标志值00。
改该顶点标志值遍历一次图中所的方法进行拓扑排序。
排序结束后,对相邻矩阵棕的顶点进行入度为0时,输出错误信息,结束程序。
否则,排序成功,输有顶点的标志值,当有一个标志值为0出排序后的顶点序列。
程序的流程:
输入课程总数,再输入相应数量的课程编号及每个课程的先修课初始化模块1)(程,用这些信息初始化一个有向图。
:
对有向图进行拓扑排序。
)拓扑排序模块(2根据有向图是否为空输出。
为空时,输出拓扑排序结果;不为空时输出模块:
)(3输出输入错误提示。
各层次模块之间的调用关系
拓初扑始排输化序出模模模块块块
三、详细设计
物理数据类型
由于用户输入的课程个数不定,所以存储拓扑排序后的顶点的个数不定,由此用链式队列来存储排序后的顶点。
为了检查图中是否有回路,把每一个顶点的标志值初始化为0。
(一)有向图的基本操作
1.初始化一个有向图
Graphm(intnumVert)
{
inti,j;
numVertex=numVert;//顶点数
numEdge=0;
初始化标志数组//mark=newint[numVert];
for(i=0;imark[i]=0;//每一个顶点的标志值初始化为0
matrix=(int**)newint*[numVertex];
for(i=0;imatrix[i]=newint[numVertex];//构建一个相邻矩阵
for(i=0;ifor(j=0;jmatrix[i][j]=0;
}
2.有向图的销毁
~Graphm()
{
delete[]mark;
for(inti=0;idelete[]matrix[i];
delete[]matrix;//销毁相邻矩阵
}
3.获取第一个邻居
intfirst(intv)//返回该点的第一条邻边
{
inti;
for(i=0;iif(matrix[v][i]!
=0)returni;//当顶点和顶点i有边时,返回顶点i的值
returni;
}
intnext(intv1,intv2)//获得v1的邻居v2
{
inti;
for(i=v2+1;iif(matrix[v1][i]!
=0)returni;
returni;
}
4.其他基本操作
voidsetEdge(intv1,intv2)//设置有向图的边
{
if(matrix[v1][v2]==0)
numEdge++;
matrix[v1][v2]=1;
}
intgetMark(intv)//获取顶点标记的值
{returnmark[v];}
intsetMark(intv,intval)//设置访问的标记
{mark[v]=val;}
拓扑排序)二(
找到第一个入度为0的点存入队列中,从有向图中删去此顶点以及所有以它为尾的弧,再在这些点中找入度为0的点。
重复上述操作,直至图空,或者图不空但找不到无前驱的顶点为止,此时返回该队列。
queuetopsort(GraphmG,queueQ,queueL,intn)
{
intcount[100];
intv,w,i;
for(v=0;v{count[v]=0;}
for(v=0;vfor(w=G.first(v);wcount[w]++;
for(v=0;vif(count[v]==0)//找到度为0的点
{Q.push(v);G.setMark(v,1);}//顶点进队列,并更改顶点标志值为1
while(Q.size()!
=0)
{
i=Q.front();
Q.pop();
L.push(i);
for(w=G.first(i);w{
count[w]--;//顶点度减一
if(count[w]==0)//找到度为0的点
{Q.push(w);G.setMark(w,1);}//顶点进队列,并更改顶点标志值为1
}
}
returnL;//返回存放排序后顶点的队列
}
(三)队列基本操作
//压入队列
boolpop(char*&it)
{
if(length()==0)returnfalse;
it=front->elem;
qnode*ltemp=front;
front=front->next;
deleteltemp;
if(front==NULL)rear=NULL;
size--;
returntrue;
}
//出队列
(constchar*&it){
pushbool
if(rear==NULL)
front=rear=newqnode(it,NULL);
else//append
{
rear->next=newqnode(it,NULL);
rear=rear->next;
}
size++;
returntrue;
}
//获取队列长度
intsize()const
{returnsize;}
最后,判断图中是否有回路。
可以通过遍历图中的每一个顶点的标记值,如果有一个为0,那么说明图中存在回路。
for(i=0;i{if(G.getMark(i)==0)//为0时表示该顶点未经过拓扑排序
捻畯?
尼课程输入错误!
教学计划编制失败,请重新输入。
<exit(0);}}
算法的具体步骤
创建一个数组存储顶点信息,再构建一个邻接矩阵存储输入的课程编号(顶点),和课程先修关系(有向边)构成的有向图的信息,然后对邻接矩阵中的图的信息进行拓扑排序,把排序结果存放在一个队列中。
如果一次排序结束后,遍历顶点标志值有为0,输出输入错误提示,结束程序;否则,输出队列中存储的课程编号序列。
流程图如下:
输入顶点数nNT.CreatGraphm(n)iYN号程编输入课iYget(v1);n2先修关系入输strcpy(v,v1);i++T.setEdge(n2,i);queueS;S=topsort(T);拓扑排序Y列点序输出顶iYYN束结误错T.getMark()==0?
伪代码如下,
charv[100][5];
charv1[4],v2[4];
GraphmT;
queueS;
cin.get(n);//输入课程总数n
T.CreatGraphm(n);
cin.get(v);//输入每门课的编号,保存在*v[4]数组中
for(i=0;i{
cout<;
cin>>ch;
while(ch=='T')
{
GetNum(n2);//输入先修课程编号
T.setEdge(n2,i);//n2在前表示先修的顺序
潣瑵?
是否有直接先修的课程(T/F):
;
cin>>ch;
}
}
S=topsort(T,Q,L,n);//对图T进行拓扑排序,排序序列存储在队列中返回到S
潣瑵?
教学计划编制完成,课程修读顺序为:
<printout(S);//输出排序后的顶点序列
}
算法的时空分析及改进设想
因为图的邻接矩阵是一个|V|×|V|矩阵,所以邻接矩阵的空间代价为Θ(|V|^2),对于有n个顶点的和E条弧的有向图而言,对此图的拓扑排序算法时间复杂度为Θ(V+E)
输入和输出的格式
输入:
1.输入课程数n----cin.get(n);
潣瑵?
输入课程总数:
;
cin<2.输入每门课的课程编号----cin.get(v);
for(i=0;i{
潣瑵?
输入每门课的课程编号:
<cin.get();
cin.getline(v1,4);
strcpy(v[i],v1);//要用字符串拷贝函数,用等号不能正确的赋值!
!
}
3.获得先修的课程编号----GetNum(n2);
潣瑵?
先修课程编号:
;
cin.get();
in.getline(v2,4);
n2=getNum(v,n,v2);
输出:
1.编制成功,把队列S中的顶点序列输出。
----printout(S);
for(i=0;i{
j=S.front();
cout<S.pop();
}
编制失败,图中有回路,输出错误信息,结束程序。
2.
if(G.getMark(i)==0)//为0时表示该顶点未经过拓扑排序
{
潣瑵?
课程输入错误!
教学计划编制失败,请重新输入。
<exit(0);
}
四、测试数据
编制成功,003->001->002
编制失败,001->002,002->001
附上完整代码
#include
#include
#include
usingnamespacestd;
classedge
{
public:
intvertex,weight;//顶点和权
edge(){vertex=-1;weight=-1;}
edge(intv,intw){vertex=v;weight=w;}
};
classGraphm
{
private:
intnumVertex,numEdge;
int**matrix;//矩阵
int*mark;
public:
Graphm(){}
voidCreatGraphm(intnumVert)
{
inti,j;
numVertex=numVert;//vertex顶点数
numEdge=0;
mark=newint[numVert];
for(i=0;imark[i]=0;
matrix=(int**)newint*[numVertex];
for(i=0;imatrix[i]=newint[numVertex];
for(i=0;ifor(j=0;jmatrix[i][j]=0;
}
~Graphm()
{
delete[]mark;
for(inti=0;idelete[]matrix[i];
delete[]matrix;
}
intn(){returnnumVertex;}
inte(){returnnumEdge;}
intfirst(intv)
{
inti;
for(i=0;iif(matrix[v][i]!
=0)returni;
returni;
}
intnext(intv1,intv2)//获得v1的邻居v2
{
inti;
for(i=v2+1;iif(matrix[v1][i]!
=0)returni;
returni;
}
voidsetEdge(intv1,intv2)
{
if(matrix[v1][v2]==0)
numEdge++;
matrix[v1][v2]=1;
}
intgetMark(intv)
{returnmark[v];}
voidsetMark(intv,intval)
{mark[v]=val;}
};
queuetopsort(GraphmG,queueQ,queueL,intn)
{
intcount[100];
intv,w,i;
for(v=0;v{
count[v]=0;
}
for(v=0;vfor(w=G.first(v);wcount[w]++;
for(v=0;vif(count[v]==0)
{Q.push(v);G.setMark(v,1);}
while(Q.size()!
=0)
{
i=Q.front();
Q.pop();
L.push(i);
for(w=G.first(i);w{
count[w]--;
if(count[w]==0)
{Q.push(w);G.setMark(w,1);}
}
}
for(i=0;i{
if(G.getMark(i)==0)//为0时表示还未被删除,图不为空
{
潣瑵?
课程输入错误!
教学计划编制失败,请重新输入。
<exit(0);
}
}
returnL;
}
intgetNum(char(*vert)[5],intn,char*s)
{
for(inti=0;iif(strcmp(vert[i],s)==0)
returni;}
}
voidmain()
{
intn;
charv[100][5];
charv1[4],v2[4];
charch;
inti,n2,j;
GraphmT;
queueQ;
queueL;
queueS;
;
输入课程总数:
挠畯?
尼
cin>>n;
T.CreatGraphm(n);
for(i=0;i{
潣瑵?
输入每门课的课程编号:
<cin.get();
cin.getline(v1,4);
strcpy(v[i],v1);//要用字符串拷贝函数,用等号不能正确的赋值!
!
}
for(i=0;i{
cout<;
cin>>ch;
while(ch=='T')
{
潣瑵?
先修课程编号:
;
cin.get();
cin.getline(v2,4);
n2=getNum(v,n,v2);
T.setEdge(n2,i);//n2在前表示先修的顺序
潣瑵?
是否有直接先修的课程(T/F):
;
cin>>ch;
}
//cout<}
cout<S=topsort(T,Q,L,n);
//////////////
cout<挠畯?
尼教学计划编制完成,课程修读顺序为:
<for(i=0;i{
j=S.front();
cout<S.pop();
}
cout<}