一摞烙饼的排序
有大小不一的一摞饼在你的一只手上,现在你需要将饼由大到小翻转,即最大尺寸的饼在底部,最小尺寸的饼在最上面,而且你只能用一只手进行翻转。千言万语不如一张图。
上图是三张饼的翻转,你是否能写出一个程序,对于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;
}