编程之美 1.3 一摞烙饼的排序

一摞烙饼的排序

    有大小不一的一摞饼在你的一只手上,现在你需要将饼由大到小翻转,即最大尺寸的饼在底部,最小尺寸的饼在最上面,而且你只能用一只手进行翻转。千言万语不如一张图。
在这里插入图片描述
    上图是三张饼的翻转,你是否能写出一个程序,对于n块大小不一的烙饼,输出最优化的翻饼过程?
解题思路:
1、 最笨的办法,一次翻转一个未排序中最大的饼需要两次翻转,那么n个饼就需要2*(n-1)次。当然这是最多的次数。
2、 万一翻转之前部分饼的顺序已经是排好的了,如此一想,这种情况下那翻转次数肯定会更少,但是这种情况还是比较难找。
3、 要找出最好的办法,最笨的办法,穷举吧!当然穷举的过程为了缩短次数,当以已经翻转的次数大于2*(n-1)那么那次穷举就可以停止了。跟进一步对当前序列进行翻转次数预估,预估次数加上已翻转次数大于2*(n-1),那么那次穷举就可以停止了。
    穷举的思路:上图。从左到有是:顶端-底端。
在这里插入图片描述

源码:
#include <iostream>

using namespace std;

class CPrefixSorting
{
private:
	int* m_CakeArray; // 烙饼信息数组
	int m_nCakeCnt;   // 烙饼个数
	int m_nMaxSwap;   // 最多交换次数,根据前面的推断,这里最多为 m_nCakeCnt*2
	int* m_SwapArray; // 交换结果数组
	int* m_ReverseCakeArray; // 当前翻转烙饼信息数组
	int* m_ReverseCakeArraySwap; // 当前翻转烙饼交换结果数组
	int m_nSearch; // 当前搜索次数信息
public:
	CPrefixSorting()
	{
		m_nCakeCnt = 0;
		m_nMaxSwap = 0;
	}

	~CPrefixSorting()
	{
		delete m_CakeArray;
		delete m_SwapArray;
		delete m_ReverseCakeArray;
		delete m_ReverseCakeArraySwap;
	}

	// 初始化烙饼信息:个数
	void init(int *pCakeArray, int nCakeCnt)
	{
		// 初始化烙饼数组
		m_nCakeCnt = nCakeCnt;
		m_CakeArray = new int[nCakeCnt];
		for (int i = 0; i < m_nCakeCnt; ++i)
		{
			m_CakeArray[i] = pCakeArray[i];
		}

		// 设置最多交换次数信息
		m_nMaxSwap = UpperBound(m_nCakeCnt);

		// 初始化交换结果数组
		m_SwapArray = new int[m_nMaxSwap];

		// 初始化中间交换结果信息
		m_ReverseCakeArray = new int[m_nCakeCnt];
		for (int i = 0; i < m_nCakeCnt; ++i)
		{
			m_ReverseCakeArray[i] = m_CakeArray[i];
		}
		m_ReverseCakeArraySwap = new int[m_nMaxSwap];
	}


	void run(int*pCakeArray, int nCakeCnt)
	{
		init(pCakeArray, nCakeCnt);

		m_nSearch = 0;
		search(0);
	}

	// 输出烙饼具体翻转次数
	void output()
	{
		for (int i = 0; i < m_nMaxSwap; ++i)
		{
			cout << m_SwapArray[i]<<" ";
		}

		cout << endl << "|Search Times| :" << m_nSearch <<endl;
		cout << "Total Swap times = " << m_nMaxSwap << endl;
	}

	// 寻找当前翻转的上界
	int UpperBound(int nCakeCnt)
	{
		return nCakeCnt * 2;
	}

	// 寻找当前翻转的下界
	int lowerBound(int* pCakeArray, int nCakeCnt)
	{
		int t, ret = 0;
		
	// 根据当前数组的排序信息情况来判断最少需要交换多少次
// 要求饼大小间隔为1,故有待改进
		for (int i = 1; i < nCakeCnt; ++i)
		{
			// 判断位置相邻的两个烙饼,是否为尺寸排序上相邻的
			t = pCakeArray[i] - pCakeArray[i - 1];
			if (t == 1 || t == -1)
			{

			}
			else
			{
				ret++;
			}
		}

		return ret;
	}

	// 排序的主函数
	void search(int step)
	{
		int i, nEstimate;
		m_nSearch++;

		// 估算这次搜素所需的最小交换次数
		nEstimate = lowerBound(m_ReverseCakeArray, m_nCakeCnt);
// 预估次数加已翻转次数大于m_nMaxSwap就退出
		if (step + nEstimate > m_nMaxSwap)
			return;

		// 如果已经排好序,即翻转完成,记录每次翻转的饼序号
		if (isSorted(m_ReverseCakeArray, m_nCakeCnt))
		{
			// 记录最少翻转步骤
			if (step < m_nMaxSwap)
			{
				m_nMaxSwap = step;
				for (i = 0; i < m_nMaxSwap; ++i)
				{
					m_SwapArray[i] = m_CakeArray[m_ReverseCakeArraySwap[i]];
				}
			}
			return;
		}

		// 递归进行翻转
		for (i = 1; i < m_nCakeCnt; ++i)
		{
			// 翻转顶部饼到第i个饼
			reverse(0, i);

			// 记录翻转第i个饼序号
			m_ReverseCakeArraySwap[step] = i;

			// 进行下一轮翻转穷举
			search(step + 1);

			//若第step+1步的翻转失败,回复到第step步的翻转结果
			reverse(0, i);
		}
	}

	// 是否排好序
	bool isSorted(int* pCakeArray, int nCakeCnt)
	{
		for (int i = 1; i < nCakeCnt; ++i)
		{
			if (pCakeArray[i - 1] > pCakeArray[i])
			{
				return false;
			}
		}

		return true;
	}

	// 翻转烙饼信息
	void reverse(int nBegin, int nEnd)
	{
		int i, j, t;

		// 翻转烙饼信息
		for (i = nBegin, j = nEnd; i < j; ++i, --j)
		{
			t = m_ReverseCakeArray[i];
			m_ReverseCakeArray[i] = m_ReverseCakeArray[j];
			m_ReverseCakeArray[j] = t;
		}
	}
};


int main()
{
	int CakeArray[] = {4,2,3,1,5,6,7,9,8}; //顶端-底端
	int size = sizeof(CakeArray) / sizeof(CakeArray[0]);
	CPrefixSorting a;
	a.run(CakeArray,size);
	a.output();
	getchar();
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值