排列树的回溯算法框架首先需要掌握,即巧妙的利用x[i]进行swap()操作,能够保证子问题不会再次扫描到父亲层以及之上的结点。
排列树比较重要的是要理解恢复现场操作,从子问题回溯归来的时候,需要将所有相关的变量恢复到进入子问题之前,这样才能不影响其他的选择下的子问题计算。
解决问题的思路还是总结一下: 问题求什么? 如果要我们动手做这件事,我们怎么去做? 然后抽象成数学模型。
举例此题: 我们要求每个任务在机器2上完成的时间的和,每个任务都要完成,而且要求和最小。
要解决这个问题,我们就要想怎么安排这些任务的顺序才能使和最小。 但是,我们没有办法一眼就看出来,只有从第一个任务位置开始,给它随便安排一个任务,累加一下时间和,然后再随便放置一个任务到第二个位置,累加一下时间和,一直到所有任务都放置完全,这时候的时间和如果比之前算出来的都小,那么这种分配顺序就是我们想要的。
所以很容易就想到了这是一种排列树的结构,有n!个叶结点,即有n!种解,每种解对应一个调度时间和,从中挑一个最小的。
那么,这时候我们就可以利用回溯算法框架来写出这个问题的算法,每一个任务位置都有几种任务可以放置上去,放置上去以后计算一些时间方面的问题,然后解决下一个任务位置,一直到所有任务位置填满,看一下时间和是不是更优秀一些。
但是,这样做还是不够的,因为回溯算法的特点就是利用了剪枝函数来缩小解空间树的大小。 很明显,如果我们把所有任务都这么安排一遍,第一个位置有n种,第二个位置有n-1种,第三个位置有n-2种.....第n个位置有n-(n-1)=1种, 所以最终有n!种结果,从这里边去挑一个最优的就变成了穷举法了,n!是很可怕的量级。
在这个问题里,剪枝函数是一个显式的约束函数,如果当前结点处选入某任务,totalTIme>=bestTime,那么这个任务就不能选入,也就没必要继续递归它的子树了,所以剪去。 如果加入后totalTime<bestTime,那么这条路径依旧有潜力发展为最小调度和,所以进入递归.