书中的题目描述:
星期五的晚上,一帮同事在希格玛大厦附近的“硬盘酒吧”多喝了几杯。程序员多喝了几杯之后谈什么呢?自然是算法问题。有个同事说:
“我以前在餐馆打工,顾客经常点非常多的烙饼。店里的饼大小不一,我习惯在到达顾客饭桌前,把一摞饼按照大小次序摆好——小的在上面,大的在下面。由于我一只手托着盘子,只好用另一只手,一次抓住最上面的几块饼,把它们上下颠倒个个儿,反复几次之后,这摞烙饼就排好序了。
我后来想,这实际上是个有趣的排序问题:假设有n块大小不一的烙饼,那最少要翻几次,才能达到最后大小有序的结果呢?
你能否写出一个程序,对于n块大小不一的烙饼,输出最优化的翻饼过程呢?
分析与解法
这个排序问题非常有意思,首先我们要弄清楚解决问题的关键操作——“单手每次抓几块饼,全部颠倒”。

每次我们只能选择最上方的一堆饼,一起翻转。而不能一张张地直接抽出来,然后进行插入,也不能交换任意两块饼子。这说明基本的排序办法都不太好用。那么怎么把这n个烙饼排好序呢?
由于每次操作都是针对最上面的饼,如果最底层的饼已经排序,那我们只用处理上面的n-1个烙饼。这样,我们可以再简化为n-2、n-3,直到最上面的两个饼排好序。
用javascript实现下:演示@google code
不要用ie折磨自己了,chrome最快,ff勉强
<script type="text/javascript">
Array.prototype.clone=function(){
return this.slice(0,this.length);
};
function FlapJack(arr){
this._original=arr;
//工作数组,会经常变化的
this._work=arr.clone();
//最终有序数组
this._result=[];
//最优翻转步鄹
this._reverseStep=[];
//记录枚举状态的中间翻转步鄹
this._workReverseStep=[];
//总共调用了多少次search操作
this._totalSearchCount=0;
//初始上限,最多2.(n-1)次翻转可以有序
this.maxStep=2*(arr.length-1);
}
FlapJack.prototype.run=function(){
this.search(0);
};
FlapJack.prototype.search = function(step){
this._totalSearchCount++;
//当前工作数组达到有序的最少翻转此数
var nEstimate = this.lowerBound(this._work);
//已经超过当前上限,忽略,剪枝
if(nEstimate + step > this.maxStep || step >= this.maxStep) return;
//已经完成要求有序
if(this.isSorted(this._work)) {
//当前步鄹为最优,记录当前步鄹详情
if(step < this.maxStep) {
this.maxStep = step;
this._reverseStep=this._workReverseStep.slice(0,step);
this._result=this._work.clone();
return;
}
}
//枚举搜索
for(var i=1;i<this._work.length;i++) {
//0...i 翻转
this.reverse(this._work,0,i);
//记录翻转步鄹
this._workReverseStep[step]=i;
//继续枚举搜索
this.search(step+1);
//还原继续搜索
this.reverse(this._work,0,i);
}
};
FlapJack.prototype.isSorted=function(arr) {
for(var i = 1; i < arr.length; i++){
if(arr[i-1] > arr[i]) {
return false;
}
}
return true;
};
//下界计算,凡是有两个位置相邻尺寸不相邻的都需要至少一次翻转将它们分开
FlapJack.prototype.lowerBound=function(arr){
var ret=0;
for(var i=1;i<arr.length;i++) {
if(Math.abs(arr[i]-arr[i-1])!=1)
ret++;
}
return ret;
};
//
FlapJack.prototype.reverse = function(arr,begin,end) {
var i=begin,
j=end,
t=0;
for(;i<j;i++,j--) {
t=arr[i];
arr[i]=arr[j];
arr[j]=t;
}
};
if(!window['consoleH']) {
consoleH={
log:function(msg){
document.writeln(msg+"<br />");
//alert(msg);
}
};
}
(function main(){
var fj=new FlapJack([3, 2, 1, 6, 5, 4 ,9, 8 ,7, 0]);
fj.run();
consoleH.log("初始结果 [ "+[3, 2, 1, 6, 5, 4 ,9, 8 ,7, 0]+" ]");
consoleH.log("搜索次数 "+fj._totalSearchCount);
consoleH.log("最终有序结果 [ "+fj._result+" ]");
consoleH.log("最优翻转次数 "+fj.maxStep);
consoleH.log("最优翻转步鄹 "+fj._reverseStep);
})();
</script>
584

被折叠的 条评论
为什么被折叠?



