加载中…
个人资料
  • 博客等级:
  • 博客积分:
  • 博客访问:
  • 关注人气:
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
正文 字体大小:

有关考试安排的算法(一):不冲突的算法

(2016-01-20 11:27:08)
分类: 算法
2009-12-08 08:57 1385人阅读 评论(0) 收藏 举报

版权声明:本文为博主原创文章,未经博主允许不得转载。

    这是一个困扰我很久的问题,今天早上洗脸的时候突然想起,事实上也没有一开始想象中的那么困难,不管三七二十一、四七二十八,先记录下来再说。

 

    要求:

    1、学校在某个学期一共开放了N门课程由学生自由选择,每一个学生可以选择一门或几门课程进行学习。

    2、在进行期末考试时,同一学生选修的两门课程不能安排在同一个时间考。

 

    原本还想要求在最短时间内考完,但只要加上一个“最”字,实现起来就比较困难了,所以,先简化一下,只要同一学生选修的两门课程不能安排在同一时间内考试即可。

    学生选课表(SelectCourse)的结构如下图所示:

http://p.blog.csdn.net/images/p_blog_csdn_net/smallfools/EntryImages/20091208/1.jpg

    其中:

    Id:自动增长的主键

    StudentId:学号

    StudentName:姓名

    CourseName:选课的课程名

 

    考场安排表(Exam)的结构如下图所示:

http://p.blog.csdn.net/images/p_blog_csdn_net/smallfools/EntryImages/20091208/2.jpg

    其中:

    Id:自动增长的主键

    ExamScreenings:考场场次,既第几场考试

    Course:考试课程名

    ExamNum:该门课程的选课人数

 

    算法如下所示:

    1、设置考场中最多可同时考试的人数。

    2、统计每门课程的考试人数,放入临时表中。

    3、先在临时表中选出选课人数最多的那门课程,当然,必须要先确定考场中最多可同时考试的人数要大于单门课程的最多选课人数。

    4、将考试场次加1,并将选课人数最多的课程(当前课程)添加到考场安排表里(Exam),然后将该门课程从临时选课表中删除。

    5、然后将考试最多可同时考试的人数减去已经安排的考试课程的人数,得到考场中还可以安排的考试人数,即剩余考试人数(@RemainNum)

    6、选择所有选课人数少于或等于剩余考试人数的课程,并放到游标里。

    7、循环从游标里取出课程,判断选择该课程的学生是否和当前考试场次中已安排的课程的选课学生有冲突(既是否同一学生在同一时间内参加两门考试)。如果没有冲突,则将该课程放入考场安排表中,并从临时表中删除该门课程。

    8、重新统计剩余考试人数,返回到第5步骤,直到所有选课人数少于剩余考试人数,或着所有考试都有冲突不能安排为止。

    9、返回第3步骤,直到所有课程都已经安排为止。

 

    具体实现的SQL语句如下所示:

[c-sharp] view plaincopy
  1. truncate table Exam  
  2. --考场中可同时容纳的考生人数  
  3. declare @MaxNumber int  
  4. set @MaxNumber 1500  
  5. --考试场次  
  6. declare @ExamScreenings int  
  7. set @ExamScreenings  
  8. --单场考试还能安排多少人数  
  9. declare @RemainNum int  
  10. --最始化:可安排人数与考场可容纳人数相同  
  11. set @RemainNum @MaxNumber  
  12. --最后一次已安排的考试课程名  
  13. declare @LastCourseName varchar(50)  
  14. set @LastCourseName null  
  15. --统计每门课程的考试人数,并放入临时表中  
  16. select CourseName,count(id) as ExamNum   
  17. into #tempSelectCourse  
  18.     from SelectCourse  
  19.         group by  CourseName  
  20.         order by ExamNum desc  
  21. --显示临时表中的数据  
  22. select from #tempSelectCourse  
  23. --开始循环  
  24. while 1=1  
  25. begin  
  26.     --当前选择的考试课程名  
  27.     declare @CurrentCourseName varchar(50)  
  28.     set @CurrentCourseName null  
  29.       
  30.     --当前课程的选课人数  
  31.     declare @CurrentExamNum int  
  32.     --在临时表中选出选课最多的那门课程(假设该门课程的选课人数小于考场可安排的人数)  
  33.     select top @CurrentExamNum ExamNum, @CurrentCourseName CourseName from #tempSelectCourse order by ExamNum desc  
  34.     --如果当前选择的考试课程名为空,则说明在该场次考试中已经没有可安排的课程  
  35.     if @CurrentCourseName is null  
  36.         begin  
  37.             --跳出循环  
  38.             break  
  39.         end  
  40.     else  
  41.         --否则说明还有可安排的课程  
  42.         begin  
  43.             --考试场次加1  
  44.             set @ExamScreenings @ExamScreenings  
  45.             --将当前选择的课程添加到考场安排表里  
  46.             insert Exam (ExamScreenings,Course,ExamNum) values (@ExamScreenings,@CurrentCourseName,@CurrentExamNum)  
  47.             --从临时表中删除该门课程  
  48.             delete #tempSelectCourse where CourseName @CurrentCourseName  
  49.             NextCourse:  
  50.             --当前考场还能安排多少人进行考试  
  51.             set @RemainNum @RemainNum @CurrentExamNum  
  52.             --将选课人数少于或等于当前考场还能安排考试人数的记录放在游标里  
  53.             declare aa cursor for  
  54.                 select ExamNum,CourseName from #tempSelectCourse where ExamNum<=@RemainNum order by ExamNum desc  
  55.             --打开游标  
  56.             open aa  
  57.             --从游标里取出一条记录  
  58.             fetch next from aa into @CurrentExamNum,@CurrentCourseName  
  59.             --判断游标里是否还有记录  
  60.             while(@@fetch_status=0)  
  61.                 begin  
  62.                     --判断选课当前课程的学生,在同一场考试中是否还有别的课程需要考试(考试冲突判断)  
  63.                     if (SELECT count(id) FROM SelectCourse   
  64.                             INNER JOIN   
  65.                                 SELECT SelectCourse.StudentId FROM Exam   
  66.                                     INNER JOIN SelectCourse ON Exam.Course SelectCourse.CourseName  
  67.                                 WHERE (Exam.ExamScreenings @ExamScreenings)  
  68.                                 AS ArrangedCourse   
  69.                             ON SelectCourse.StudentId ArrangedCourse.StudentId  
  70.                         WHERE CourseName=@CurrentCourseName)=0  
  71.                         begin  
  72.                             --如果考试不冲突,则将当前选择的课程添加到考场安排表里  
  73.                             insert Exam (ExamScreenings,Course,ExamNum) values (@ExamScreenings,@CurrentCourseName,@CurrentExamNum)  
  74.                             --从临时表中删除该门课程  
  75.                             delete #tempSelectCourse where CourseName @CurrentCourseName  
  76.                             --关闭游标,重新计算剩余的课程  
  77.                             close aa  
  78.                             deallocate aa  
  79.                             goto NextCourse  
  80.                         end  
  81.                         --获取下一条记录  
  82.                         fetch next from aa into @CurrentExamNum,@CurrentCourseName  
  83.                 end  
  84.             --关闭游标  
  85.             close aa  
  86.             deallocate aa  
  87.         end  
  88. end  
  89. --删除临时表  
  90. drop table #tempSelectCourse  
  91.       
 

    这个算法运行速度还可以,我试了13756人次131门选课,运行结果20秒左右。但是,这不是最优的算法,这131门的选课一共需要106个场次的考试,天哪,太多了!

    可以说,这个算法失败了!

    有人知道更好的算法吗?

 

原创不容易,转载请注明出处。http://blog.csdn.net/smallfools/archive/2009/12/08/4961307.aspx

0

阅读 收藏 喜欢 打印举报/Report
  

新浪BLOG意见反馈留言板 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 产品答疑

新浪公司 版权所有