问题描述:
烙饼问题可以简化为对一段由n个无重复的整数组成的无序数组a[n]进行排序。排序要求每次只能对a[0]~a[i]部分的数组进行翻转(0 < i < n),最终完成排序。
输入:数组大小n;n个整数。
输出:最小递归查找次数m;每次翻转位置j0j1…jm-1。
问题思考:
烙饼排序这部分,主要考量的是对递归函数的使用。
而搜索上界与搜索下界则可以一定程度上提高代码的运行效率,减少搜索次数。
递归流程如下:
①首先进行0~i的翻转
②记录第step步翻转位置i
③然后进行下一层翻转
④调用递归返回之后,再进行一次0~i的翻转,保持数组进入本次循环时的顺序
①i++,进行0~i+1的翻转
…
for (int i = 1; i < m_nCakeCnt; i++)
{
Revert(0, i);
m_ReverseCakeArraySwap[step] = i;
Search(step + 1);
Revert(0, i);
}
稍微修改代码之后可以发现,最小翻转的解并不是唯一的。
测试输入:10,{ 5, 9, 4, 1, 2, 0, 8, 7, 6, 3 }
输出有效最优解:
14849278
15183931
15218393
15839312
19385312
19465853
19484278
19615285
|Search Times| : 159193
Total Swap times = 8
完整代码如下:
#include <stdio.h>
#include "stdafx.h"
#include "afx.h"
class CPrefixsorting
{
public:
CPrefixsorting()
{
m_nCakeCnt = 0;
m_nMaxSwap = 0;
}
void Run(int* pCakeArray, int nCakeCnt)
{
Init(pCakeArray, nCakeCnt);
m_nSearch = 0;
Search(0);
}
//输出烙饼具体翻转的次数
void Output()
{
for (int i = 0; i < m_nCakeCnt; i++)
{
printf("%d", m_ReverseCakeArray[i]);
}
printf("\n");
for (int i = 0; i < m_nMaxSwap; i++)
{
printf("%d", m_SwapArray[i]);
printf("\n");
Revert(0, m_SwapArray[i]);
for (int i = 0; i < m_nCakeCnt; i++)
{
printf("%d", m_ReverseCakeArray[i]);
}
printf("\n");
}
printf("\n |Search Times| : %d\n", m_nSearch);
printf("Total Swap times = %d\n", m_nMaxSwap);
}
private:
//初始化数组信息
void Init(int* pCakeArray, int nCakeCnt)
{
ASSERT(pCakeArray != NULL);
ASSERT(nCakeCnt > 0);
m_nCakeCnt = nCakeCnt;
//初始化烙饼数组
m_CkaeArray = new int[m_nCakeCnt];
ASSERT(m_CkaeArray != NULL);
for (int i = 0; i < m_nCakeCnt; i++)
{
m_CkaeArray[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_CkaeArray[i];
}
m_ReverseCakeArraySwap = new int[m_nMaxSwap];
}
//寻找翻转上界
int upBound(int nCakeCnt)
{
return nCakeCnt * 2;
}
//寻找翻转下界
int LowerBound(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 = LowerBound(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];
printf("%d", m_SwapArray[i]);
}
printf("\n");
}
return;
}
//递归进行翻转
for (int i = 1; i < m_nCakeCnt; i++)
{
Revert(0, i);
m_ReverseCakeArraySwap[step] = i;
Search(step + 1);
Revert(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 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_CkaeArray; //烙饼信息数组
int m_nCakeCnt; //烙饼个数
int m_nMaxSwap; //最多交换次数,根据前面的推断,这里最多为m_NCakeCnt*2
int* m_SwapArray; //交换结果数组
int* m_ReverseCakeArray; //当前翻转烙饼信息数组
int* m_ReverseCakeArraySwap; //当前翻转烙饼交换结果数组
int m_nSearch; //当前搜索次数信息
};
int main()
{
CPrefixsorting x;
int array[10] = { 5, 9, 4, 1, 2, 0, 8, 7, 6, 3 };
x.Run(array, 10);
x.Output();
getchar();
return 0;
}