一摞烙饼的问题

问题:

    星期五的晚上,一帮同事在希格玛大厦附近的“硬盘酒吧”多喝了几杯。程序员多喝了几杯之后谈什么呢?自然是算法问题。有个同事说:“我以前在餐馆打工,顾客经常点非常多的烙饼。店里的饼大小不一,我习惯在到达顾客饭桌前,把一摞饼按照大小次序摆好——小的在上面,大的在下面。由于我一只手托着盘子,只好用另一只手,一次抓住最上面的几块饼,把它们上下颠倒个个儿,反复几次之后,这摞烙饼就排好序了。我后来想,这实际上是个有趣的排序问题:假设有n块大小不一的烙饼,那最少要翻几次,才能达到最后大小有序的结果呢?”

你能否写出一个程序,对于n块大小不一的烙饼,输出最优化的翻饼过程呢?

解决方案:

《编程之美》上面给出的方法是通过递归寻找出最优方案。

递归结束条件有两个:

1)递归步骤超过最大步骤数;

2)已经有序。

但由于递归过程步骤过多,需要对其进行剪枝:

1)定上界:假设每次只将最大的翻到最下面(跟冒泡类似),至多需要2次翻转,总共有n-1个需要翻转(最后一个不用翻了),那么最多需要2*(n-1)次翻转;当然,为了减少更多不必要的遍历,每次找到一个实现了有序的步骤数step,则将max更新为该step。

2)定下界:每次递归时,计算当前所在状态下至少还需要多少次翻转,设为lowerbound, 如果当前已经翻转的步骤数step+lowerbound(已经翻转步骤数+当前状态下至少还需要的步骤数)超过了上届,则可以结束该次递归了。

下面是结合该想法,自己写的一个程序:

//============================================================================
// Name        : 3_version1.cpp
// Author      : yahohi
// Version     :
// Copyright   : All rights reserved
// Description : Ansi-style
//============================================================================

#include <iostream>
using namespace std;

#define size 5
int array[size] = {1,2,3,4,5};
int nMax = 2*(size - 1);

static int move[2*(size - 1)];

bool isSorted(int len);
void reverse(int begin, int end);
void println(int a[], int len);
int LowerBound(int a[], int s);

void Search(int step)
{
	int lowerbound = LowerBound(array, size);
	if ((lowerbound + step) > nMax)
		return;

	if (isSorted(size))
	{
		cout << endl<< "step: " << step << endl;
		cout << "More details: " << endl;
		println(move, step);

		nMax = step;
		return;
	}

	//每一次都有size-1中翻的方法
	for (int i = 1;i < size;i ++)
	{
		reverse(0,i);//翻前i个饼
		move[step] = i;
		Search(step + 1);
		reverse(0,i);//恢复现场
	}
}

int LowerBound(int a[], int s)
{
	int ret = 0;
	for (int i = 0;i < s - 1; i ++)
	{
		if (abs(a[i] - a[i+1]) != 1)
			ret ++;
	}
	return ret;
}

bool isSorted(int len)
{
	bool ret = true;
	for (int i = 0;i < len-1; i ++)
	{
		if (array[i] > array[i+1])
		{
			ret = false;
			break;
		}
	}
	return ret;
}

void reverse(int begin, int end)
{
	for (int i = begin;i < (begin+end + 1)/2; i++)
	{
		int temp = array[i];
		array[i] = array[end - i];
		array[end - i] = temp;
	}
}

void println(int a[], int len)
{
	for (int i = 0;i < len; i ++)
	{
		cout << a[i] << " ";
	}
	cout<<endl;
}

int main() {
	reverse(0,1);
	reverse(0,2);
	println(array, size);

	Search(0);
	return 0;
}
其中,递归过程中的恢复现场尤为重要!

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

posted @ 2012-04-11 10:33  wangicter的博客  阅读(221)  评论(0编辑  收藏  举报