这是segmentfault中的一个问题,尝试把算法写出来了。
十间教室 六门课程教室支持课程如图所示
要求举例说明 安排甲课程时 可选择 a b c d e五间教室 假如甲课程占用b c d e教室 还剩余a 教室空闲
安排乙课程能选三间教室 首选 f g 教室 但因a 教室空闲 所以自动 把甲课程某一项移入a教室 空出公共的教室以供 乙课程使用
以此类推 后者优先 当没有教室可分配时 提示 无法分配
<?php
class ClassRoomArrange
{
/**
* 课程可用教室
* @var array
*/
private $courseInit = [
"aCourse" => ["A", "B", "C", "D", "E"],
"bCourse" => ["B", "C", "D", "E", "F", "G"],
"cCourse" => ["H", "J", "K"],
"dCourse" => ["A", "B", "C"],
"eCourse" => ["C", "D", "E", "F", "G"],
"fCourse" => ["H", "J", "K"],
];
/**
* 解决结果
* @var array
*/
private $solution = [];
/**
* 每日安排
* @var array
*/
private $dailyArrange = [];
/**
* 日常可用教室
* @var array
*/
public $availableClassroom = ["A", "B", "C", "D", "E", "F", "G", "H", "J", "K"];
public function __construct($dailyArrange)
{
$this->dailyArrange = $dailyArrange;
}
/**
* 教室安排
* @return array | boolean
*/
public function ArrangeClassroom()
{
if (empty($this->dailyArrange)) {
return false;
}
//排序优先教室
krsort($this->dailyArrange);
foreach ($this->dailyArrange as $course => $count) {
//交集得到课程可用教室
$used = [];
$intersect = array_intersect($this->availableClassroom, $this->courseInit[$course]);
if ($intersect) {
//从可用教室里面去所取需课程数
$used = array_slice($intersect, 0, $count);
}
$this->solution[$course] = $used;
//如果排位结果不满足所需数量,则尝试挪位操作
if ($surplus = $count - count($this->solution[$course])) {
//获取剩余所属教室
$z = 0;
foreach ($this->courseInit[$course] as $letter) {
//从其他的排位中尝试挪位
if (!in_array($letter, $this->solution[$course]) && $this->move($letter, [$course])) {
if (++$z > $surplus) {
break;
}
array_push($this->solution[$course], $letter);
}
}
}
//差集删除已用教室
$this->availableClassroom = array_diff($this->availableClassroom, $used);
}
return $this->solution;
}
/**
* 如果排位结果不满足所需数量,尝试挪位
* @param $letter
* @param $exclude
* @return bool
*/
private function move($letter, $exclude)
{
//从排位结果中查找可挪位的结果
foreach ($this->solution as $c => &$r) {
//如果需要移动的教室在排位结果中的,则尝试挪位
if (!in_array($c, $exclude) && in_array($letter, $r)) {
$fix = $r;
//将排除列已排序字母也作为排出项
foreach ($exclude as $e) {
$fix = array_merge($fix, $this->solution[$e]);
}
//获取可移动的教室
$initSurplus = array_diff($this->courseInit[$c], $fix);
//该教室在某个安排中是不可移动了,则说明不能再挪了
if(empty($initSurplus)){
return false;
}
//取出教室往后移
array_push($exclude, $c);
foreach ($initSurplus as $moveLetter) {
//如果移动后会影响其他排位则再尝试挪位
if ($this->checkMove($moveLetter, $c) || $this->move($moveLetter, $exclude)) {
$r = array_merge(array_diff($r, [$letter]));
array_push($r, $moveLetter);
return true;
}
}
}
}
return false;
}
/**
* 检查是否影响其他排位
* @param $a
* @param $c
* @return bool
*/
private function checkMove($a, $c)
{
foreach ($this->solution as $cc => $rr) {
if ($cc !== $c && in_array($a, $rr)) {
return false;
}
}
return true;
}
}
//周一需要安排哪些课程,一个课程需要安排多少节
$mondayArrange = ["aCourse" => 4, "bCourse" => 2, "cCourse" => 1, "eCourse"=>1, "fCourse" => 2];
$classRoomArrange = new ClassRoomArrange($mondayArrange);
var_dump($classRoomArrange->ArrangeClassroom());
/*
返回结果:
array (size=6)
'fCourse' =>
array (size=1)
0 => string 'H' (length=1)
'eCourse' =>
array (size=3)
0 => string 'E' (length=1)
1 => string 'F' (length=1)
2 => string 'G' (length=1)
'dCourse' =>
array (size=1)
0 => string 'C' (length=1)
'cCourse' =>
array (size=2)
0 => string 'J' (length=1)
1 => string 'K' (length=1)
'bCourse' =>
array (size=1)
0 => string 'D' (length=1)
'aCourse' =>
array (size=2)
0 => string 'A' (length=1)
1 => string 'B' (length=1)
*/