真是个有意思的题目,烙饼,lb,鲁滨,哈哈。决定下个11账号就叫 烙饼_刘 了。
书上的思路其实很简单,用了状态空间搜索里面最常见的分支限界法,用循环和递归穷举所有可能的情况,然后约束上界和下界来进行剪枝。上界越小,下界越大,剪枝效果越好。接口如下:
class Lbsort{
public:
Lbsort():m_laobings(0), m_pCount(0), m_swapInfo(0), t_laobings(0), t_swapInfo(0), max_swapCount(0), m_searchTimes(0){}
~Lbsort(){
if(m_laobings != NULL){
delete m_laobings;
m_laobings = 0;
}
if(m_swapInfo != NULL){
delete m_swapInfo;
m_swapInfo = 0;
}
if(t_laobings != NULL){
delete t_laobings;
t_laobings = 0;
}
if(t_swapInfo != NULL){
delete t_swapInfo;
t_swapInfo = 0;
}
}
void doSort(int*, int);//主接口
void printResults();//打印结果
private:
void search(int);//主排序
int upbound(int);//初始化时返回上界
int lowbound(int*, int);//剪枝时使用的下界
void init(int*, int);//系统状态的初始化
bool isSorted(int*, int);//判断是否已经排序完成
void doReverse(int*, int);//翻转最上面那部分烙饼
int* m_laobings;//存放原始烙饼的编号
int m_pCount;//存放原始烙饼的数目
int* m_swapInfo;//存放每次交换的位置的最终结果
int* t_laobings;//存放烙饼编号中间结果
int* t_swapInfo;//存放交换位置的中间结果
int max_swapCount;//存放交换次数的上届
int m_searchTimes;//存放当前的交换次数
};
实现也非常简单:
void Lbsort::doSort(int* laobings, int count){
//Assert(laobings != NULL && count > 0);
init(laobings, count);
search(0);
}
void Lbsort::printResults(){
using std::cout;
using std::endl;
cout<<"Total search times: "<<m_searchTimes<<endl;
cout<<"Min swap times: "<<max_swapCount<<endl;
for(int i=0;i<max_swapCount;i++)
cout<<m_swapInfo[i]<<" ";
cout<<endl;
}
void Lbsort::search(int step){
++m_searchTimes;
int tp_lowbound = lowbound(t_laobings, m_pCount);
if(tp_lowbound+step>=max_swapCount)
return;
if(isSorted(t_laobings, m_pCount)){
if(step < max_swapCount){
max_swapCount = step;
memcpy(m_swapInfo, t_swapInfo, max_swapCount*sizeof(int));
}
return;
}
for(int i=1;i<m_pCount;i++){
doReverse(t_laobings, i);
t_swapInfo[step] = i;
search(step+1);
doReverse(t_laobings, i);
}
}
int Lbsort::upbound(int count){
return 2*(count - 1);
}
int Lbsort::lowbound(int* laobings, int count){
int tp_low = 0, t;
for(int i=count-1;i>0;i--){
t = laobings[i] - laobings[i-1];
if(!(t == 1 || t == -1)){
tp_low++;
}
}
return tp_low;
}
void Lbsort::init(int* laobings, int count){
m_laobings = new int[count];
t_laobings = new int[count];
max_swapCount = upbound(count);
m_swapInfo = new int[max_swapCount];
t_swapInfo = new int[max_swapCount];
//Assert(m_laobings != NULL && t_laobings != NULL && m_swapInfo != NULL && t_swapInfo != NULL);
memcpy(m_laobings, laobings, count*sizeof(int));
memcpy(t_laobings, laobings, count*sizeof(int));
m_pCount = count;
m_searchTimes = 0;
}
bool Lbsort::isSorted(int* laobings, int count){
for(int i=1;i<count;i++){
if(laobings[i-1] > laobings[i])
return false;
}
return true;
}
void Lbsort::doReverse(int* laobings, int pos){
for(int i=0, j=pos;i<j;i++,j--){
int tp = laobings[i];
laobings[i] = laobings[j];
laobings[j] = tp;
}
}
分支限界的复杂度是很高的,书上提到了动态规划但是没有实现,上网搜了一下,有人分析出这个问题是不能使用动态规划和贪心的,主要原因是不满足最优子结构,即当前的最优解并不一定是全局的最优解。在这种情况下,翻转需要最小的次数,并不代表它可以带来全局的最少翻转次数。