编程之美-1.3-烙饼排序问题

问题描述:
烙饼问题可以简化为对一段由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;
}
    原文作者:Cqy_Chaos
    原文地址: https://blog.csdn.net/cqysmyg_1/article/details/83411611
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞