您的位置:首页 > 其它

一摞烙饼的排序问题--读书笔记(2)

2015-01-29 14:48 363 查看
问题描述:一摞大小不一的饼,由于一只手托着盘子,只好用另一只手,一次抓住最上面的几块饼,把它们上下颠倒个,反复几次使烙饼安装由小到大排好序。假设有n块大小不一的饼,最少需要翻几次使烙饼排好序。

分析与解法:

首先,经过两次翻转可以把最大的烙饼翻转到最下面,因此,最多需要把上面的n-1个烙饼依次翻转两次。那么至多需2(n-1)次翻转就可以把所有烙饼排好序。当然还有更高效的方法,考虑每次翻转的时候,把两个本来应该相邻的烙饼尽可能的换到一起,当所有的烙饼都换到一起时,实际上已经完成了排序。因此,可以通过递归来进行求解。

递归的第一个退出条件是已经排好序,还有就是翻转次数多于2(n-1)就退出。在翻转的过程中,可以看看当前的烙饼数组的排序情况如何,然后利用这些信息帮助减少翻转次数。

每个状态还应该有翻转的最小次数,这个下限值可以这样确定:从最后一个位置开始,往前找到第一个与最终结果位置不同的烙饼编号(也就是说排除最后几个已经就位的烙饼),从该位置到第一个位置,计算相邻的烙饼的编号不连续的次数,再加上1。

代码如下:

#include<stdio.h>
#include<assert.h>

class CPrefixSorting
{
public:
CPrefixSorting()
{
m_nCakeCnt = 0;
m_nMaxSwap = 0;
}

//计算烙饼翻转信息
//@param
//pCakeArray 存储烙饼索引数组
//nCakeCnt 烙饼个数

void Run(int* pCakeArray,int nCakeCnt)
{
Init(pCakeArray,nCakeCnt);

m_nSearch = 0;
Search(0);
}

//输出烙饼具体翻转次数

void Output()
{
for(int i = 0; i < m_nMaxSwap; i++)
{
printf("%d", m_SwapArray[i]);
}
printf("\n |Search Times| : %d\n", m_nSearch);
printf("Total Swap times = %d\n", m_nMaxSwap);
}

private:
//
//初始化数组信息
//@param
//pCakeArray   存储烙饼索引数组
//nCakeCnt  烙饼个数
//
void Init(int* pCakeArray,int nCakeCnt)
{
assert(pCakeArray != NULL);
assert(nCakeCnt > 0);

m_nCakeCnt = nCakeCnt;

//初始化烙饼数组
m_CakeArray = new int[m_nCakeCnt];
assert(m_CakeArray != NULL);
for(int i = 0; i < m_nCakeCnt; i++)
{
m_CakeArray[i] = pCakeArray[i];
}

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

//初始化交换结果数组
m_SwapArray = new int[m_nMaxSwap];
assert(m_SwapArray != NULL);

//初始化中间交换结果信息
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];
}

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

//
//寻找当前翻转的下界
//
//
int Lower_Bound(int* pCakeArray,int nCakeCnt)
{
int t, ret = 0;

//根据当前数组的排序信息情况来判断最少需要交换多少次
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 = Lower_Bound(m_ReverseCakeArray,m_nCakeCnt);
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_ReverseCakeArraySwap[i];
}
return;
}

//递归进行翻转
for(int i = 1; i < m_nCakeCnt; i++)
{
Revert(0, i);
m_ReverseCakeArraySwap[step] = i;
Search(step + 1);
Revert(0, i);
}
}
//
//true: 已经排好序
//false: 未排序
//
bool IsSorted(int* pCakeArray,int nCakeCnt)
{
for(int i = 1; i < nCakeCnt; i++)
{
if(pCakeArray[i-1] > pCakeArray[i])
{
return false;
}
}
return true;
}

//
//翻转烙饼信息
//
void Revert(int nBegin,int nEnd)
{
assert(nEnd > nBegin);
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;
}
}
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;               //当前搜索次数信息
};

int main()
{
CPrefixSorting cpfs;
int cakeArray[] = {3,2,1,6,5,4};
cpfs.Run(cakeArray,6);
cpfs.Output();
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: