圖結構的應用:教學計劃編制問題

有兩種方法:一種沒有學分排序,另一種考慮了學分排序,較推薦棧中排好序的方法,貪心更完全
測試數據文本:
C1 程序設計基礎 # 2
C2 離散數學 C1 3
C3 數據結構 C1,C2 4
C4 彙編語言 C1 3
C5 語言的設計和分析 C3,C4 2
C6 計算機原理 C11 3
C7 編譯原理 C3,C5 4
C8 操作系統 C3,C6 4
C9 高等數學 # 7 
C10 線性代數 C9 5
C11 普通物理 C9 2
C12 數值分析 C1,C9,C10 3
#include<iostream>
#include<fstream>
#include<iomanip>
#define XQ_num 12               //最大學期數
#define Total_Book_num 100        //最大課程數
#define TRUE 1
#define ERROR 0
using namespace std;
struct Book                     //節點的數據類型
{
 char Book_num[4];                                   //不能爲3,因爲會把後面的字符串也讀取進來
 char Book_nam[20];
 int study_Score;
 int in_Degree;                   //指入度點的個數
 int state;                        //標記是否已經修過這門課程
 Book *next;                   //指先行課的地址
 char book[40];             //先修課程字符串
 int num;                      //記錄是第幾門課
 int n;                       //記錄有幾門先行課
};
struct Build_Graph             //用鄰接表的方法構建圖,數組的位置則是記錄該節點鄰接了所有得點
{
 Book *graph;              
 int n;									// int *length.....length數組的長度
 void build_Graph(int n1)  //課程的門數---(注意這個不能設爲構造函數,否則後面棧中的數據結構會引用這個數據結構沒有構造參數)
 {
  graph=new Book [n1];			//每個數組爲一門課程並且爲鄰接表的頭節點
										//length=new int [n1];           //指頭節點每個鏈表的長度,同數組存儲
  for(int i=0;i<n1;i++)
  {
	  graph[i].next=NULL;
								// length[i]=0;                     //記錄每門課的先行課的門數(未修的門數),不需要,用入度決定有幾門先行可
  }
  n=n1;
 }
 void Book_insert(Book p,int i)									 //插入每門要修的課程
 {
	 graph[i]=p;
	  graph[i].num=i+1;							//記錄是第幾門課,從第1門開始
	  graph[i].in_Degree=0;
	  graph[i].state=1;                     //如果狀態爲1時則是未被訪問過,0則表示是訪問過
	  graph[i].n=0;
 }
 void Build_Graph_insert(Book &p,int j,int n)					//在j之後插入節點,在第n門課
 {
	 Book *pp=&graph[n-1];
	 for(int i=1;i<j;i++)
		 pp=pp->next;
	 pp->next=&p;
	 graph[n-1].in_Degree++;							//length[n]++;
 }
 void Fu_Zhi()                               //對每個變量進行賦值
 {
	 int i;
	 for(i=0;i<n;i++)
	 {
		 Fenjie(graph[i].book,graph[i]);
	 }
 }
 void Fenjie(char a[],Book &c)                        //爲門課程建立鄰接表
{
	char b[5];
	int length=strlen(a);
	int i=0,j=0,k=0;        
	while(i<length)
	{
		Book *t=new Book;			 //從第1門開始記錄
		while(a[i]!=','&&a[i]!='#'&&i<length)
		{
			b[j]=a[i];
			i++;j++;
		}
		if(a[i]=='#'){break;}           //注意這裏存放的先後順序
		b[j]='\0';
		*t=graph[PiPei(b)-1];               //c[k]數組記錄每一門課程的先行課的數字型編號
		(*t).next=NULL;
		(*t).in_Degree=0;
		k++;                                  //
		Build_Graph_insert(*t,k,c.num);      //因爲是從自己本身開始算起,所以要從k+1門課後傳遞指針
		if(a[i]==','){i++;j=0;continue;}
		i++;j=0;
	}
	c.n=k;
}
int PiPei(char a[])
{
	if(!strcmp(a,"C1")) return 1;
	else
		if(!strcmp(a,"C2")) return 2;
		else
			if(!strcmp(a,"C3"))return 3;
			else
				if(!strcmp(a,"C4"))return 4;
				else
					if(!strcmp(a,"C5"))return 5;
					else
						if(!strcmp(a,"C6"))return 6;
						else
							if(!strcmp(a,"C7"))return 7;
							else
								if(!strcmp(a,"C8"))return 8;
								else
									if(!strcmp(a,"C9"))return 9;
									else
										if(!strcmp(a,"C10"))return 10;
										else
											if(!strcmp(a,"C11"))return 11;
											else
												if(!strcmp(a,"C12"))return 12;
}
 void display1()									//輸出所有課程表的信息
 {
	cout<<setiosflags(ios::left);
	cout<<setw(15)<<"課程編號"<<setw(30)<<"課程名稱"<<setw(20)<<"先決條件"<<"學分"<<endl;
	cout<<"--------------------------------------------------------------------------------"<<endl;
	for(int i=0;i<n;i++)
	{
		cout<<setw(15)<<graph[i].Book_num<<setw(30)<<graph[i].Book_nam<<setw(20)<<graph[i].book<<graph[i].study_Score<<endl;
	}
 }
 void display2()									 //輸出鄰接表的函數
 {
	 int i,j;
	 Book *t;
	 for(i=0;i<n;i++)
	 {
		 
		 t=&graph[i];
		 if(graph[i].in_Degree==0)cout<<graph[i].Book_num<<endl;
		 else
			 cout<<graph[i].Book_num<<"-->";
		 for(j=1;j<=graph[i].in_Degree;j++)
		 {
			 t=t->next;
			 if(j==graph[i].in_Degree)cout<<t->Book_num<<endl;
			 else
			 {
				cout<<t->Book_num<<"-->";
			 }
		 }
	 }
 }
 void Destory()
 {
	 delete graph;
 }
};

struct Stack        //構造棧
{
	Book *base;
	int top;
	int length;								//記錄棧中數據的長度
	//Build_Graph graph1;
	//int sum1;							 //學期總數
	//int top_score;						//每個學期的學分上線
	Stack(int n)
	{
		base=new Book [n];   ////因爲有多少門課是已經在Build_Graph結構中的n變量中包含,因爲最大的課程數爲total_booknum
		top=-1;
		length=0;
	}
	void Destory_Stack()
	{
		delete base;
	}
	void Clear_Stack()
	{
		top=-1;
		length=0;
	}
	bool Test_Stack_Empty()					//棧空爲true,不空爲false
	{
		if(top==-1)return true;
		else return false;
	}
	Book Get_Top()							//獲得棧頂元素並把棧頂元素輸出棧
	{
		top--;
		length--;
		return base[top+1];
	}
	int Get_Top_Study_Score()
	{
		int t;
		t=base[top].study_Score;
		return t;
	}
	void Push(Book p)
	{
		top++;
		length++;
		base[top]=p;
	}
	int Length_Stack()
	{
		return length;
	}
};

void Topological_Sort(Stack s,Build_Graph g,int a)          //a是總的課程數目
{
  cout<<"--------------------------------------------------------------------------------"<<endl;
  int sum,top_score,ii,i,j,k,average;
  cout<<"                                 學期總數:";cin>>sum;
  cout<<"                             每學期的學分上線爲:";cin>>top_score;
  if(sum>XQ_num){cout<<"                             輸入的學期數超出了範圍!"<<endl; return ;}
  if(top_score>Total_Book_num){cout<<"                             輸入的課程數超出了範圍!"<<endl;return ;}
  cout<<"--------------------------------------------------------------------------------"<<endl;
  cout<<"                              課程編排的結果爲:"<<endl;
  cout<<"--------------------------------------------------------------------------------"<<endl;
   average=a/sum;average+=1;            //每個學期選的課數小於等於average
  int count1=0,count2=0;           //count1記錄學期的個數,第幾個學期,count2記錄等於average學期的個數
  int sum_course=0;
  for(ii=0;ii<sum;ii++)			//有幾個學期就循環多少次
  {
	  for(i=0;i<a;i++)                //每個學期找入度爲0並且沒有被訪問過節點進棧
	  {
		  if((g.graph[i].in_Degree==0)&&(g.graph[i].state==1))
			  s.Push(g.graph[i]);
	  }
	  count1++;
	  Book *pp=new Book;
	  int sum_score=0;                  //記錄每個學期
	  int ss=0;           //記錄每個學期選課的門數總的學分
	  cout<<"第"<<count1<<"個學期所選的課程爲:      ";
	  while((!s.Test_Stack_Empty())&&(ss<average)&&((sum_score+s.Get_Top_Study_Score())<=top_score)&&(count2<average))          //安排一個學期的課程
	  {
		  pp=&s.Get_Top();
		  cout<<(*pp).Book_nam<<"	";
		  sum_course++;
		  g.graph[(*pp).num-1].state=0;             //注意是原來的圖的入讀數-1
		  ss++;
		  sum_score+=(*pp).study_Score;
		  Book *p;
		  for(j=0;j<a;j++)               //入度爲0的節點的後序節點的入讀-1
		  {
			  if(j==((*pp).num-1))continue;
			  int n=g.graph[j].in_Degree;
			  if(n!=0)
			  {
				  p=&g.graph[j];
				  for(k=0;k<(g.graph[j].n);k++)             //找一個鏈表中相匹配的課程
				  {
					  p=p->next;
					  if((*pp).num==p->num){g.graph[j].in_Degree--;}
				  }
			  }
		  }
	  }
	  s.Clear_Stack();
	  if(ss==average)count2++;
	  cout<<endl;
  }
  if(sum_course==a)cout<<"課程編排成功!"<<endl;
  else cout<<"課程編排失敗!"<<endl;
  cout<<"--------------------------------------------------------------------------------"<<endl;
}
int main()
{
 int total_booknum,n;
 cout<<"--------------------------------------------------------------------------------"<<endl;
 cout<<"                           歡迎進入課程編排系統                               "<<endl;
 cout<<"--------------------------------------------------------------------------------"<<endl;
con:cout<<"                 是否開始進行課程編排,是請按1,退出請按0:";
	cin>>n;
 while(n==1)
 {
	 system("cls");
  cout<<"                             該專業開設的課程數:";cin>>total_booknum;
  cout<<"--------------------------------------------------------------------------------"<<endl;
  cout<<"輸入每門課的課程信息(文件的讀入)";
  ifstream in("課程信息.txt");
  if(!in)
  {
   cerr<<"打開文本-課程信息.txt-有誤!"<<endl;
   exit(1);
  }
  Build_Graph Graph;
  Graph.build_Graph(total_booknum);
  int i;Book p;
  for(i=0;i<total_booknum;i++)
  {
	  in>>p.Book_num>>p.Book_nam>>p.book>>p.study_Score;
	  Graph.Book_insert(p,i);
  }
  in.close();
  cout<<"要編排的課程信息如下(沒有先行課的先行課屬性爲#):"<<endl;
	  Graph.display1();                                             //課程信息的輸出
	  Graph.Fu_Zhi();
	  cout<<"--------------------------------------------------------------------------------"<<endl;
	  cout<<"課程的鄰接表(右邊爲該門課的先行課)爲:"<<endl;
	  Graph.display2();
	  Stack T_sort(total_booknum);
	  Topological_Sort(T_sort,Graph,total_booknum);
	  goto con;
 }
 if((n!=1)&&(n!=0)){cout<<"輸入的數據不符合,請重新輸入!"<<endl;goto con;}
 return 0;
}
排序後:
#include<iostream>
#include<fstream>
#include<iomanip>
#define XQ_num 12               //最大學期數
#define Total_Book_num 100        //最大課程數
#define TRUE 1
#define ERROR 0
using namespace std;
struct Book                     //節點的數據類型
{
 char Book_num[4];                                   //不能爲3,因爲會把後面的字符串也讀取進來
 char Book_nam[20];
 int study_Score;
 int in_Degree;                   //指入度點的個數
 int state;                        //標記是否已經修過這門課程
 Book *next;                   //指先行課的地址
 char book[40];             //先修課程字符串
 int num;                      //記錄是第幾門課
 int n;                       //記錄有幾門先行課
};
struct Build_Graph             //用鄰接表的方法構建圖,數組的位置則是記錄該節點鄰接了所有得點
{
 Book *graph;              
 int n;									// int *length.....length數組的長度
 void build_Graph(int n1)  //課程的門數---(注意這個不能設爲構造函數,否則後面棧中的數據結構會引用這個數據結構沒有構造參數)
 {
  graph=new Book [n1];			//每個數組爲一門課程並且爲鄰接表的頭節點
										//length=new int [n1];           //指頭節點每個鏈表的長度,同數組存儲
  for(int i=0;i<n1;i++)
  {
	  graph[i].next=NULL;
								// length[i]=0;                     //記錄每門課的先行課的門數(未修的門數),不需要,用入度決定有幾門先行可
  }
  n=n1;
 }
 void Book_insert(Book p,int i)									 //插入每門要修的課程
 {
	 graph[i]=p;
	  graph[i].num=i+1;							//記錄是第幾門課,從第1門開始
	  graph[i].in_Degree=0;
	  graph[i].state=1;                     //如果狀態爲1時則是未被訪問過,0則表示是訪問過
	  graph[i].n=0;
 }
 void Build_Graph_insert(Book &p,int j,int n)					//在j之後插入節點,在第n門課
 {
	 Book *pp=&graph[n-1];
	 for(int i=1;i<j;i++)
		 pp=pp->next;
	 pp->next=&p;
	 graph[n-1].in_Degree++;							//length[n]++;
 }
 void Fu_Zhi()                               //對每個變量進行賦值
 {
	 int i;
	 for(i=0;i<n;i++)
	 {
		 Fenjie(graph[i].book,graph[i]);
	 }
 }
 void Fenjie(char a[],Book &c)                        //爲門課程建立鄰接表
{
	char b[5];
	int length=strlen(a);
	int i=0,j=0,k=0;        
	while(i<length)
	{
		Book *t=new Book;			 //從第1門開始記錄
		while(a[i]!=','&&a[i]!='#'&&i<length)
		{
			b[j]=a[i];
			i++;j++;
		}
		if(a[i]=='#'){break;}           //注意這裏存放的先後順序
		b[j]='\0';
		*t=graph[PiPei(b)-1];               //c[k]數組記錄每一門課程的先行課的數字型編號
		(*t).next=NULL;
		(*t).in_Degree=0;
		k++;                                  //
		Build_Graph_insert(*t,k,c.num);      //因爲是從自己本身開始算起,所以要從k+1門課後傳遞指針
		if(a[i]==','){i++;j=0;continue;}
		i++;j=0;
	}
	c.n=k;
}
int PiPei(char a[])
{
	if(!strcmp(a,"C1")) return 1;
	else
		if(!strcmp(a,"C2")) return 2;
		else
			if(!strcmp(a,"C3"))return 3;
			else
				if(!strcmp(a,"C4"))return 4;
				else
					if(!strcmp(a,"C5"))return 5;
					else
						if(!strcmp(a,"C6"))return 6;
						else
							if(!strcmp(a,"C7"))return 7;
							else
								if(!strcmp(a,"C8"))return 8;
								else
									if(!strcmp(a,"C9"))return 9;
									else
										if(!strcmp(a,"C10"))return 10;
										else
											if(!strcmp(a,"C11"))return 11;
											else
												if(!strcmp(a,"C12"))return 12;
}
 void display1()									//輸出所有課程表的信息
 {
	cout<<setiosflags(ios::left);
	cout<<setw(15)<<"課程編號"<<setw(30)<<"課程名稱"<<setw(20)<<"先決條件"<<"學分"<<endl;
	cout<<"--------------------------------------------------------------------------------"<<endl;
	for(int i=0;i<n;i++)
	{
		cout<<setw(15)<<graph[i].Book_num<<setw(30)<<graph[i].Book_nam<<setw(20)<<graph[i].book<<graph[i].study_Score<<endl;
	}
 }
 void display2()									 //輸出鄰接表的函數
 {
	 int i,j;
	 Book *t;
	 for(i=0;i<n;i++)
	 {
		 
		 t=&graph[i];
		 if(graph[i].in_Degree==0)cout<<graph[i].Book_num<<endl;
		 else
			 cout<<graph[i].Book_num<<"-->";
		 for(j=1;j<=graph[i].in_Degree;j++)
		 {
			 t=t->next;
			 if(j==graph[i].in_Degree)cout<<t->Book_num<<endl;
			 else
			 {
				cout<<t->Book_num<<"-->";
			 }
		 }
	 }
 }
 void Destory()
 {
	 delete graph;
 }
};

struct Stack        //構造棧
{
	Book *base;
	int top;
	int length;								//記錄棧中數據的長度
	//Build_Graph graph1;
	//int sum1;							 //學期總數
	//int top_score;						//每個學期的學分上線
	Stack(int n)
	{
		base=new Book [n];   ////因爲有多少門課是已經在Build_Graph結構中的n變量中包含,因爲最大的課程數爲total_booknum
		top=-1;
		length=0;
	}
	void Destory_Stack()
	{
		delete base;
	}
	void Clear_Stack()
	{
		top=-1;
		length=0;
	}
	bool Test_Stack_Empty()					//棧空爲true,不空爲false
	{
		if(top==-1)return true;
		else return false;
	}
	Book Get_Top()							//獲得棧頂元素並把棧頂元素輸出棧
	{
		top--;
		length--;
		return base[top+1];
	}
	int Get_Top_Study_Score()
	{
		int t;
		t=base[top].study_Score;
		return t;
	}
	void Push(Book p)
	{
		top++;
		length++;
		base[top]=p;
	}
	int Length_Stack()
	{
		return length;
	}
	void PX_MAX_MIN()       //排序,從小到大
	{
		int i,j;Book t;
		for(i=0;i<(length-1);i++)
		{
			int max=base[i].study_Score;
			for(j=i+1;j<length;j++)
			{
				if(max<base[j].study_Score)
				{
					t=base[i];
					base[i]=base[j];
					base[j]=t;
				}
			}
		}
	}
};

void Topological_Sort(Stack s,Build_Graph g,int a)          //a是總的課程數目
{
  cout<<"--------------------------------------------------------------------------------"<<endl;
  int sum,top_score,ii,i,j,k,average;
  cout<<"                                 學期總數:";cin>>sum;
  cout<<"                             每學期的學分上線爲:";cin>>top_score;
  if(sum>XQ_num){cout<<"                             輸入的學期數超出了範圍!"<<endl; return ;}
  if(top_score>Total_Book_num){cout<<"                             輸入的課程數超出了範圍!"<<endl;return ;}
  cout<<"--------------------------------------------------------------------------------"<<endl;
  cout<<"                              課程編排的結果爲:"<<endl;
  cout<<"--------------------------------------------------------------------------------"<<endl;
   average=a/sum;average+=1;            //每個學期選的課數小於等於average
  int count1=0,count2=0;           //count1記錄學期的個數,第幾個學期,count2記錄等於average學期的個數
  int sum_course=0;
  for(ii=0;ii<sum;ii++)			//有幾個學期就循環多少次
  {
	  for(i=0;i<a;i++)                //每個學期找入度爲0並且沒有被訪問過節點進棧
	  {
		  if((g.graph[i].in_Degree==0)&&(g.graph[i].state==1))
			  s.Push(g.graph[i]);
	  }
	  count1++;
	  Book *pp=new Book;
	  int sum_score=0;                  //記錄每個學期
	  int ss=0;           //記錄每個學期選課的門數總的學分
	  cout<<"第"<<count1<<"個學期所選的課程爲:      ";
	  s.PX_MAX_MIN();
	  while((!s.Test_Stack_Empty())&&(ss<average)&&((sum_score+s.Get_Top_Study_Score())<=top_score)&&(count2<average))          //安排一個學期的課程
	  {
		  s.PX_MAX_MIN();
		  pp=&s.Get_Top();
		  cout<<(*pp).Book_nam<<"	";
		  sum_course++;
		  g.graph[(*pp).num-1].state=0;             //注意是原來的圖的入讀數-1
		  ss++;
		  sum_score+=(*pp).study_Score;
		  Book *p;
		  for(j=0;j<a;j++)               //入度爲0的節點的後序節點的入讀-1
		  {
			  if(j==((*pp).num-1))continue;
			  int n=g.graph[j].in_Degree;
			  if(n!=0)
			  {
				  p=&g.graph[j];
				  for(k=0;k<(g.graph[j].n);k++)             //找一個鏈表中相匹配的課程
				  {
					  p=p->next;
					  if((*pp).num==p->num){g.graph[j].in_Degree--;}
				  }
			  }
		  }
	  }
	  s.Clear_Stack();
	  if(ss==average)count2++;
	  cout<<endl;
  }
  if(sum_course==a)cout<<"課程編排成功!"<<endl;
  else cout<<"課程編排失敗!"<<endl;
  cout<<"--------------------------------------------------------------------------------"<<endl;
}
int main()
{
 int total_booknum,n;
 cout<<"--------------------------------------------------------------------------------"<<endl;
 cout<<"                           歡迎進入課程編排系統                               "<<endl;
 cout<<"--------------------------------------------------------------------------------"<<endl;
con:cout<<"                 是否開始進行課程編排,是請按1,退出請按0:";
	cin>>n;
 while(n==1)
 {
	 system("cls");
  cout<<"                             該專業開設的課程數:";cin>>total_booknum;
  cout<<"--------------------------------------------------------------------------------"<<endl;
  cout<<"輸入每門課的課程信息(文件的讀入)";
  ifstream in("課程信息.txt");
  if(!in)
  {
   cerr<<"打開文本-課程信息.txt-有誤!"<<endl;
   exit(1);
  }
  Build_Graph Graph;
  Graph.build_Graph(total_booknum);
  int i;Book p;
  for(i=0;i<total_booknum;i++)
  {
	  in>>p.Book_num>>p.Book_nam>>p.book>>p.study_Score;
	  Graph.Book_insert(p,i);
  }
  in.close();
  cout<<"要編排的課程信息如下(沒有先行課的先行課屬性爲#):"<<endl;
	  Graph.display1();                                             //課程信息的輸出
	  Graph.Fu_Zhi();
	  cout<<"--------------------------------------------------------------------------------"<<endl;
	  cout<<"課程的鄰接表(右邊爲該門課的先行課)爲:"<<endl;
	  Graph.display2();
	  Stack T_sort(total_booknum);
	  Topological_Sort(T_sort,Graph,total_booknum);
	  goto con;
 }
 if((n!=1)&&(n!=0)){cout<<"輸入的數據不符合,請重新輸入!"<<endl;goto con;}
 return 0;
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章